CDQ分治筆記 例題

2022-07-23 04:36:10 字數 2750 閱讀 1111

看這樣乙個問題:在乙個三維座標系中,有若干個點,每個點都有對應的座標 \((x_i , y_i , z_i)\) ,我們要對於每個點求所有滿足 \(x_j <=x_i, y_j <= y_i , z_j <= z_i\) 的 j 的數量。

考慮在二維平面上怎麼做:我們可以將所有點按照 x 座標排序,再用乙個樹狀陣列或者其他資料結構維護 y 座標,每次讀到乙個點就統計答案,然後將這個點加入樹狀陣列。這樣可以同時保證 x,y 座標都滿足條件。

如果這時再多一維,我們就沒辦法再用這種方法排序了,按照一維排序必然會打亂另外兩維。這時我們考慮cdq分治,先按 x 座標排序,然後對序列進行分治,每次分出左右兩段子序列,這時我們可以發現,左序列中每個點的 x 座標小於右序列中點的 x 座標,這時分別將左右序列按照 y 座標排序。統計答案時,只更新右序列中點的答案,只在樹狀陣列中加入左序列的點。

模版:p3810 【模板】三維偏序(陌上花開)

#include #include #include #include #include using namespace std;

const int n = 100005;

const int m = 200005;

int c[m], n, maxk, nn, ans[n];

struct node e[n];

int lowbit(int x)

void add(int x, int k)

int query(int x)

bool cmp1(node x, node y)

void cdq(int l, int r)

while(i <= mid) add(e[i].c, e[i].tot), p[++pt] = e[i++];

while(j <= r) e[j].num += query(e[j].c), p[++pt] = e[j++];

for(int t = l; t <= mid; t++) add(e[t].c, -e[t].tot);//樹狀陣列清空

for(int t = 1, u = l; t <= pt && u <= r; t++, u++) e[u] = p[t];//更新序列

}int main()

sort(e + 1, e + 1 + nn, cmp1);//先按照第一維排序

n = 1;

for(int i = 2; i <= nn; i++)

cdq(1, n);

for(int i = 1; i <= n; i++)

for(int i = 0; i < nn; i++) printf("%d\n", ans[i]);

return 0;

}

例題1 :p3157 [cqoi2011]動態逆序對

考慮如和將其轉化為三維偏序問題,我們要計算刪除乙個點對答案對貢獻,即刪除的這個點左邊比它大的點的數量和右邊比他小的點的數量,這樣三維分別是刪除順序,序列中位置,元素大小。

#include #include #include #include #include using namespace std;

typedef long long lld;

const int n = 100005;

const int m = 50005;

int n, m, pos[n], ansper[n], ansnxt[n];

struct node e[n], a[n];

int c[n];

int lowbit(int x)

void add(int x, int k)

int query(int x)

void cdq_per(int l, int r)

}while(i <= mid) add(e[i].y, 1), p[++pt] = e[i++];

while(j <= r)

for(int k = l; k <= mid; k++) add(e[k].y, -1);

for(int t = 1, u = l; t <= pt && u <= r; t++, u++) e[u] = p[t];

}void cdq_nxt(int l, int r)

}while(i <= mid) add(a[i].y, 1), p[++pt] = a[i++];

while(j <= r)

for(int k = l; k <= mid; k++) add(a[k].y, -1);

for(int t = 1, u = l; t <= pt && u <= r; t++, u++) a[u] = p[t];

}int main()

for(int i = 1, x; i <= m; i++)

for(int i = 1; i <= n; i++) a[i] = e[n - i + 1];

cdq_per(1, n);

for(int i = 1; i <= n; i++) e[i] = a[n - i + 1];

cdq_nxt(1, n);

lld ans_sum = 0;

for(int i = 1; i <= n; i++)

int k = 1;

while(k <= m)

return 0;

}

例題2 :p4169 [violet]天使玩偶/sjy擺棋子

cdq分治 筆記

部落格觀賞效果更佳 github cnblogs 這個演算法用於解決三維偏序問題。三維偏序 給定 n nn 個三元組 ai bi,ci a i,b i,c i ai b i c i 求同時滿足滿足 ai aj,b i bj ci cja i le a j,b i le b j,c i le c j ...

學習筆記 CDQ分治

分治,考慮前一半對後一半的影響。和一般分治不太相同的思想是,一般分治不分誰對誰的影響,跨mid的都要統計。全域性變數統計 而cdq貌似要落腳到前一半對後一半的影響上,也就是貢獻在後一半統計,由前一半產生。大概使用情況 1.三維偏序 2.優化dp 3.這個裡面有。注意處理三維情況的巧妙性。heoi20...

學習筆記 CDQ分治

聽娜姐講完fft,一臉懵逼,還是來講講 cdq分治 吧。解決 帶時間軸的更改和查詢 問題。首先我們要知道,這個演算法是離線的,還是利用遞迴進行求解的。把讀入的n個操作都按照時間軸排列好。把時間軸劈開,分為 l,mid 和 mid 1,r 兩部分。開始當前層cdq l,r 的求解前先進行cdq l,m...