CDQ 分治学习笔记
Working…
本文由 Pretharp 编写,转载需注明出处,禁止用于任何形式的商业用途。
1 引入
1.1 算法相关
CDQ 分治准确而言不是某种算法,而是一种思想。分治,顾名思义,就是面对大问题时,将问题划分为若干小问题,再逐一 忽略 解决。值得一提的是,CDQ 分治通常是离线算法,接下来,我们将以例题讲解 CDQ 分治。
2 例题
2.1 例题 1
题目大意:
给定 \(n\) 个元素,每个元素有三个属性 \(a_i,b_i\) 和 \(c_i\)。令 \(f(i)\) 表示满足 \(a_j \le a_i\) 且 \(b_j \le bi\) 且 \(c_j \le c_i\) 且 \(j \neq i\) 的 \(j\) 的个数。求对于 \(d \in [0,n)\) 中,\(f(i)=d\) 的 \(i\) 的个数。
分析:
这是一道三维偏序题,我们用 CDQ 分治的思想解决。首先,我们将所有元素以 \(a_i\) 为第一关键字升序排序,那么我们对于区间 \([l,r]\),显然,区间内 \([l,mid]\) 的元素将有可能对 \((mid,r]\) 的元素的答案做出贡献,因为对于 \(i \in [l,mid],j \in (mid,r]\) 必然有 \(a_i \le a_j\)(\(mid\) 是区间中点)。接下来,我们可以将 \([l,mid]\) 和 \((r,mid]\) 内的元素分别以 \(b_i\) 为第一关键字排序,此时答案已然变为二维偏序问题,用树状数组维护即可求得答案。
参考代码(部分):
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 5, M = 2e5 + 5;
int tn, n, k, buc[M];
struct Ele {
int x, y, z, ans, same;
} tmp[N], a[N];
bool cmpWithX(const Ele &x, const Ele &y) {
return (x.x == y.x ? (x.y == y.y ? x.z < y.z : x.y < y.y) : x.x < y.x);
}
bool cmpWithY(const Ele &x, const Ele &y) {
return (x.y == y.y ? x.z < y.z : x.y < y.y);
}
namespace FenwickTree {...}
ttFT::ttFT fwt; // 这两行代码就是定义了一个树状数组。
void CDQ(int l, int r) {
if(l >= r) {
return;
}
int mid = l + r >> 1;
CDQ(l, mid), CDQ(mid + 1, r);
sort(a + l, a + 1 + mid, cmpWithY),
sort(a + 1 + mid, a + 1 + r, cmpWithY);
int i = mid + 1, j = l;
for(; i <= r; i ++) {
while(j <= mid && a[j].y <= a[i].y) {
fwt.modify(a[j].z, a[j].same);
j ++;
}
a[i].ans += fwt.query(a[i].z);
}
for(int o = l; o < j; o ++) {
fwt.modify(a[o].z, -a[o].same);
}
}
signed main() {
cin >> tn >> k;
for(int i = 1; i <= tn; i ++) {
cin >> tmp[i].x >> tmp[i].y >> tmp[i].z;
}
sort(tmp + 1, tmp + 1 + tn, cmpWithX);
for(int i = 1, cnt = 0; i <= tn; i ++) {
cnt ++;
if(
tmp[i].x == tmp[i + 1].x
&& tmp[i].y == tmp[i + 1].y
&& tmp[i].z == tmp[i + 1].z
) {
continue;
}
a[++ n] = tmp[i], a[n].same = cnt, cnt = 0;
}
CDQ(1, n);
for(int i = 1; i <= n; i ++) {
buc[a[i].ans + a[i].same - 1] += a[i].same;
}
for(int i = 0; i < tn; i ++) {
cout << buc[i] << endl;
}
return 0;
}
题目大意:
给定一个 \(1 \sim n\) 的排列,接下来按照顺序依次删除 \(m\) 个数,问每次删除之前逆序对的数量。
分析:
其实与上一题很相似。考虑对于一个点有那些点会与其成为逆序对,不难发现对于点 \(i\),\(j\) 会对 \(i\) 产生贡献有:
- \(j\) 的删除时间比 \(i\) 晚。
- \(j\) 的位置比 \(i\) 更靠前且权值比 \(i\) 更大,或者 \(j\) 的位置更靠后且权值更小。
形式化的, \(time_j>time_i\) 且 \(pos_j<pos_i\) 且 \(val_j>val_i\),或 \(time_j>time_i\) 且 \(pos_j>pos_i\) 且 \(val_j<val_i\)(\(pos\) 是位置,\(val\) 是权值,\(time\) 是删除时间)。至此,这道题已经变成三维偏序。
代码略。

浙公网安备 33010602011771号