關於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 }