题目大意:
就是现在有n章卡片的排列p1, p2...pn, 每张卡片上写着1~n, 现在George进行n - k次remove的操作, 使得最后只剩下的牌为b1, b2...bk, 每次remove操作可以选择一段连续的区间x1, x2...xw, 然后从中移除最小的那个, 注意移除最小的那个之后剩下的卡片保持连在一起, 完成这样一次操作会得到w份香肠, 为了使最后剩下的牌的排列为b1, b2, b3 ... bk, 应该怎样选使得得到的香肠最多, 输出最多能得到的香肠份数
大致思路:
先找到贪心的思路然后就是用树状数组维护一下了...注意set当中upper_bound函数和lower_bound函数的区别
代码如下:
Result : Accepted Memory : 71500 KB Time : 1497 ms
/* * Author: Gatevin * Created Time: 2015/3/10 15:12:18 * File Name: Kotori_Itsuka.cpp */ #include<iostream> #include<sstream> #include<fstream> #include<vector> #include<list> #include<deque> #include<queue> #include<stack> #include<map> #include<set> #include<bitset> #include<algorithm> #include<cstdio> #include<cstdlib> #include<cstring> #include<cctype> #include<cmath> #include<ctime> #include<iomanip> using namespace std; const double eps(1e-8); typedef long long lint; /* * 首先不难想到贪心的思想, 每次选择移除能够移除的最小的那个数 * 这样多次选择之后区间长度综合才最大 */ int n, k; int pos[1000010];//数值为i的数的位置pos[i] bool keep[1000010];//最后i是否需要留下来为keep[i] int C[1000010]; set<int> S, T;//集合S中是需要保留的位置, 并且变化, 随着贪心接下来要选择移除的数而变化 //树状数组维护方便查询某区间当中已经被移除的数 int lowbit(int x) { return -x & x; } void add(int x, int value) { while(x <= n) C[x] += value, x += lowbit(x); return; } int ask(int L, int R) { int ret = 0; while(R) ret += C[R], R -= lowbit(R); while(L) ret -= C[L], L -= lowbit(L); return ret; } int main() { scanf("%d %d", &n, &k); int tmp; for(int i = 1; i <= n; i++) scanf("%d", &tmp), pos[tmp] = i; for(int i = 1; i <= k; i++) scanf("%d", &tmp), keep[tmp] = 1; S.insert(0); S.insert(n + 1); T.insert(0); T.insert(-n - 1); lint ans = 0; for(int i = 1; i <= n; i++)//贪心思想枚举接下来要选择删除的数 if(keep[i]) S.insert(pos[i]), T.insert(-pos[i]); else { int R = *S.upper_bound(pos[i]); //int L = *S.lower_bound(pos[i] - 1); //low_bound(x)返回的是不比x小的第一个数的地址, 不是小于x的最大的 int L = -*T.upper_bound(-pos[i]); ans += (R - L - 1); ans -= ask(L, R - 1);//这段区间(L, R)当中已经被删除的元素 add(pos[i], 1); } printf("%I64d\n", ans); return 0; }