cdq分治學習筆記
前言
感謝\(\_\_stdcall\)的講解,感謝偉大的導師\(\_tham\)提供一系列練手題
cdq分治是什麼?
國人(陳丹琦)發明的演算法,不同於一般的分治,我們常說的分治是將問題分成互不影響的幾個區間,遞迴進行處理,而所謂\(cdq\)分治,在處理一個區間時,還要計算它對其他區間的貢獻。儘管這已經脫離了分治的基本定義,但為了紀念這位偉大的人物,我們依舊將它稱為分治的一種變體。
二維偏序問題
給定\(n\)個二元組\([a,b]\),\(m\)次詢問,每次給定其中的一個二元組\([c,d]\),求滿足條件\(c<a\&d<b\)的二元組的個數
不知道怎麼做?逆序對你總會求吧?逆序對就是一種經典的二維偏序問題,我們不妨這樣轉換逆序對問題:
給定\(n\)個數,定義一個二元組為\([\)元素下標,元素值\(]\),則共有\(n\)個這樣的二元組
我們只需將約束條件改為:\(c<a\&d>b\)就行了。
那麼,解決二維偏序的一般模式,也只需要改一下合併時的那一句話就好了。
PS:啊?你忘了怎麼用歸併排序求逆序對?戳我
相同的,我們也可以用樹狀陣列來求解。複雜度同樣為\(\Theta(nlogn)\)
既然我們能用樹狀陣列來解決用\(cdq\)分治的題,那我們能不能用\(cdq\)分治來解決樹狀陣列的題目呢?當然可以,比如這道:Luogu3374 樹狀陣列1
給定一個\(n\)個元素的序列\(a\)
,初始值全部為\(0\),對這個序列進行以下兩種操作操作\(1\):格式為\(1\ x\ k\),把所有位置\(x\)的元素加上\(k\)
操作\(2\):格式為\(2 x y\),求出區間\([x,y]\)內所有元素的和。
這顯然是一道樹狀陣列模板題,考慮如何用\(cdq\)分治來解決它。
我們不妨以修改的時間為第一關鍵字,修改元素的位置為第二關鍵字。由於時間已經有序,我們定義結構體包含\(3\)個元素:\(opt,ind,val\),其中\(ind\)表示操作的位置,\(opt\)為\(1\)表示修改,\(val\)表示“加上的值”。而對於查詢,我們用字首和的思想把他分解成兩個操作:\(sum[1,y]-sum[1,x-1]\)
#include <cstdio>
#include <cstring>
#include <algorithm>
using std::min;
using std::max;
using std::swap;
using std::sort;
typedef long long ll;
const int N = 5e5 + 10, M = 5e5 + 10;
int n, m, aid, qid;
ll ans[M];
struct Query {
int ind, opt; ll val;
儲存修改取消
inline bool operator < (const Query a) const {
return ind == a.ind ? opt < a.opt : ind < a.ind;
}
}q[(M << 1) + N], tmp[(M << 1) + N];
inline void cdq (int l, int r) {
if (l == r) return ;
int mid = (l + r) >> 1;
cdq(l, mid), cdq(mid + 1, r);
int i = l, j = mid + 1, p = l; ll sum = 0;
while (i <= mid && j <= r)
if (q[i] < q[j]) {
if (q[i].opt == 1) sum += q[i].val;
tmp[p++] = q[i++];
} else {
if (q[j].opt == 2) ans[q[j].val] -= sum;
if (q[j].opt == 3) ans[q[j].val] += sum;
tmp[p++] = q[j++];
}
while (i <= mid) { if (q[i].opt == 1) sum += q[i].val; tmp[p++] = q[i++]; }
while (j <= r) {
if (q[j].opt == 2) ans[q[j].val] -= sum;
if (q[j].opt == 3) ans[q[j].val] += sum;
tmp[p++] = q[j++];
}
for (int k = l; k <= r; ++k) q[k] = tmp[k];
}
int main () {
scanf ("%d%d", &n, &m);
for (int i = 1; i <= n; ++i) {
q[++qid].ind = i, q[qid].opt = 1;
scanf("%lld", &q[qid].val);
}
int opt, ind, l, r; ll val;
for (int i = 1; i <= m; ++i) {
scanf("%d", &opt);
if (opt == 1) scanf("%d%lld", &ind, &val), q[++qid] = (Query){ind, 1, val};
else {
scanf ("%d%d", &l, &r);
q[++qid] = (Query){l - 1, 2, ++aid}, q[++qid] = (Query){r, 3, aid};
}
}
cdq(1, qid);
for (int i = 1; i <= aid; ++i)
printf("%lld\n", ans[i]);
return 0;
}