BZOJ 3295:[Cqoi2011]动态逆序对(三维偏序 CDQ分治+树状数组)
Posted Shadowdsp
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了BZOJ 3295:[Cqoi2011]动态逆序对(三维偏序 CDQ分治+树状数组)相关的知识,希望对你有一定的参考价值。
http://www.lydsy.com/JudgeOnline/problem.php?id=3295
题意:简单明了。
思路:终于好像有点明白CDQ分治处理三维偏序了。把删除操作看作是插入操作,那么可以按照插入的时间顺序看作是一维x,插入的数在原本序列的下标是一维y,插入的数本身是一维z。那么问题可以转化成每插入一个数(xx,yy,zz),求有多少个数(x,y,z)使得 x < xx,y < yy,z > zz 。一开始先对 x 进行排序,然后进行CDQ分治。这样可以干掉一维,保证随着时间递增。在分治的时候,通过标记判断那一个点属于左半区间还是右半区间,然后对 y 进行排序。如果在左半区间,那么它的 x 必定是小于 右半区间的,它所修改的结果会影响右半区间的查询,因此要去更新左半区间的元素。因为 y 是升序的,那么正着查询大于该点的 z 值的个数,就是查询可以满足 y < yy, z > zz 的条件的个数了。反着查询小于该点的 z 值的个数,即满足 y > yy, z < zz 的条件的个数。这样就可以找全插入一个数对整个数组产生的逆序对的个数了。
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 using namespace std; 5 typedef long long LL; 6 #define N 100010 7 struct node { 8 int x, y, z, f; 9 node () {} 10 node (int x, int y, int z) : x(x), y(y), z(z) {} 11 } p[N], s[N]; 12 13 int bit[N], gap, num[N], Hash[N]; 14 LL ans[N]; 15 16 bool cmpx(const node &a, const node &b) { 17 return a.x < b.x; 18 } 19 bool cmpy(const node &a, const node &b) { 20 if(a.y == b.y) return a.z < b.z; 21 return a.y < b.y; 22 } 23 24 int lowbit(int x) { return x & (-x); } 25 26 LL query(int x) { 27 LL ans = 0; 28 while(x) { ans += bit[x]; x -= lowbit(x); } 29 return ans; 30 } 31 32 void update(int x, int w) { 33 while(x <= gap) { bit[x] += w; x += lowbit(x); } 34 } 35 36 void CDQ(int l, int r) { 37 if(l == r) return ; 38 int m = (l + r) >> 1, cnt = 0; 39 CDQ(l, m); CDQ(m + 1, r); 40 for(int i = l; i <= m; i++) s[++cnt] = p[i], s[cnt].f = 0; // 在左半部分 41 for(int i = m + 1; i <= r; i++) s[++cnt] = p[i], s[cnt].f = 1; // 在右半部分 42 sort(s + 1, s + 1 + cnt, cmpy); // 根据y排序 43 for(int i = 1; i <= cnt; i++) { // 正着扫 44 if(!s[i].f) update(s[i].z, 1); // 左半部分对右半部分的查询有影响因此更新 45 else ans[s[i].x] += query(gap) - query(s[i].z); // 在[m,r]区间查询大于它的z的数量 46 } 47 for(int i = 1; i <= cnt; i++) if(!s[i].f) update(s[i].z, -1); 48 for(int i = cnt; i >= 1; i--) { // 逆着扫 49 if(!s[i].f) update(s[i].z, 1); 50 else ans[s[i].x] += query(s[i].z); // 在[m,r]区间查询小于它的z的数量 51 } 52 for(int i = 1; i <= cnt; i++) if(!s[i].f) update(s[i].z, -1); 53 } 54 55 int main() { 56 int n, m; 57 while(~scanf("%d%d", &n, &m)) { 58 int a, cnt = 0; 59 gap = 0; 60 for(int i = 1; i <= n; i++) { 61 scanf("%d", &num[i]); 62 Hash[num[i]] = i; 63 p[i] = node(0, i, num[i]); 64 if(num[i] > gap) gap = num[i]; 65 } 66 for(int i = 1; i <= m; i++) { 67 scanf("%d", &a); 68 p[Hash[a]].x = n - i + 1; 69 } 70 for(int i = 1; i <= n; i++) 71 if(p[i].x == 0) p[i].x = ++cnt; 72 sort(p + 1, p + 1 + n, cmpx); 73 memset(bit, 0, sizeof(bit)); 74 CDQ(1, n); 75 LL res = 0; 76 for(int i = 1; i <= n; i++) res += ans[i]; 77 for(int i = n; i > n - m; i--) { 78 printf("%lld\n", res); 79 res -= ans[i]; 80 } 81 } 82 return 0; 83 }
以上是关于BZOJ 3295:[Cqoi2011]动态逆序对(三维偏序 CDQ分治+树状数组)的主要内容,如果未能解决你的问题,请参考以下文章