初学CDQ分治-NEU1702
关于CDQ分治,首先需要明白分治的复杂度。
T(n) = 2T(n/2)+O(kn), T(n) = O(knlogn)
T(n) = 2T(n/2)+O(knlogn), T(n) = O(knlog^2n)
T(n) = 2T(n/2)+O(k), T(n) = O(kn)
那么我们要处理[l, r]内的询问,我们可以分别处理[l, m]和[m+1, r]的询问,然后以较小的复杂度计算出[l, m]对[m+1, r]的贡献。
最简单的cdq就是三维偏序问题。
两点(x1, y1, z1)和(x2, y2, z2),同时满足x1 < x2, y1 < y2, z1 < z2,则前面的点小于后面的点。
首先按第一维x排序。
则处理的问题变成对于排在前面的点,统计多少个点满足y维与z维同时小于该点。
CDQ分治。
假设已处理出[l, m]与[m+1, r]。对于[m+1, r]内的所有点,我们还要统计[l, m]内有多少个点相比它更小。
对[l, r]按y维排序,对z维用树状数组统计。
扫描一遍排序后的[l, r]。
若该点在排序前属于[l, m],树状数组单点修改;否则该点在排序前属于[m+1, r],统计一次。
复杂度为O(nlognlogn)
CDQ分治算法的核心就在于:去掉时间的限制,将所有查询要求发生的时刻同化,化动态修改为静态查询
(其实对于有些问题来说可以把某一维的限制通过排序看作时间限制然后运用CDQ分治)
这类分治的特殊性在于分治的左右两部分的合并,作用两部分在合并的时候作用是不同的,比如,通过左半部分的影响来更新右半部分,所以分治开始前都要按照某一个关键字排序,然后利用这个顺序,考虑一个区间[l, r]的两部分间的影响。
框架为
void cdq(int l, int r){ if(l == r) return ; int m = (l+r)/2; cdq(l, m); cdq(m+1, r); //统计[l, m]对[m+1, r]的贡献。整体排序后统计。 sort(pp+l, pp+r+1, yzx); for(int i = l; i <= r; i++) if(pp[i].x <= m) add(pp[i].z, 1); else ans[ pp[i].n ] += sum(pp[i].z); for(int i = l; i <= r; i++) if(pp[i].x <= m) add(pp[i].z, -1); }
题意:一个人的魅力值是相对于周围人来说的,如果他的颜值,内涵和智慧值同时不低于另外一个人,那么他的魅力值就会加1,给你一些人的颜值,内涵,和智慧值,请输出这些人的魅力值。
1 #include<bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 const int maxn = 1e5+10; 5 struct p{ 6 int n, x, y, z; 7 p(){} 8 p(int n, int x, int y, int z): n(n), x(x), y(y), z(z){} 9 bool operator <(const p& A) const{ 10 if(x != A.x) return x < A.x; 11 return y != A.y? y < A.y: z < A.z; 12 } 13 bool operator ==(const p& A) const{ 14 return x == A.x&&y == A.y&&z == A.z; 15 } 16 }; 17 bool yzx(p A, p B){//y z x 18 if(A.y != B.y) return A.y < B.y; 19 return A.z != B.z? A.z < B.z : A.x < B.x; 20 } 21 bool cmpn(p A, p B){//n 22 return A.n < B.n; 23 } 24 p pp[maxn]; 25 26 int c[maxn], Maxn; 27 int lowbit(int x){ return x&-x;} 28 int add(int x, int d){ 29 for(int i = x; i <= Maxn; i += lowbit(i)) 30 c[i] += d; 31 } 32 int sum(int x){ 33 int ret = 0; 34 for(int i = x; i; i -= lowbit(i)) 35 ret += c[i]; 36 return ret; 37 } 38 39 int ans[maxn]; 40 41 void cdq(int l, int r){ 42 if(l == r) return ; 43 int m = (l+r)/2; 44 cdq(l, m); 45 cdq(m+1, r); 46 sort(pp+l, pp+r+1, yzx); 47 for(int i = l; i <= r; i++) 48 if(pp[i].x <= m) 49 add(pp[i].z, 1); 50 else 51 ans[ pp[i].n ] += sum(pp[i].z); 52 for(int i = l; i <= r; i++) 53 if(pp[i].x <= m) 54 add(pp[i].z, -1); 55 } 56 57 int same[maxn];// smae[i] 表示 下标为i的ans 与 下标为same[i]相同 58 59 int main(){ 60 int T; scanf("%d", &T); 61 while(T--){ 62 int n;scanf("%d", &n); 63 for(int i = 0; i < n ; i++){ 64 pp[i].n = i; 65 scanf("%d%d%d", &pp[i].x, &pp[i].y, &pp[i].z); 66 Maxn = max(pp[i].z, Maxn); 67 } 68 sort(pp, pp+n);//x y z 69 70 for(int i = 0; i < n; ){ 71 int j = i+1; 72 while(j < n&&pp[i] == pp[j]) j++; 73 while(i < j) 74 same[ pp[i++].n ] = pp[j-1].n; 75 } 76 for(int i = 0; i < n; i++) 77 pp[i].x = i; 78 79 memset(ans, 0, sizeof(int)*(n+5) ); 80 cdq(0, n-1); 81 82 sort(pp, pp+n, cmpn); 83 for(int i = 0; i < n; i++) 84 printf("%d\n", ans[ same[ pp[i].n ] ]); 85 } 86 return 0; 87 88 }