CDQ分治
- cdq分治的原理基于以下事实:
对于每个“查询”操作,其结果
a
n
s
[
i
]
=
[
1
,
i
−
1
]
ans[i] = [1,i-1]
ans[i]=[1,i−1]中所有修改对其造成影响的叠加(这里的“叠加”需要能够比较方便的维护,例如
s
u
m
/
m
i
n
/
m
a
x
sum/min/max
sum/min/max等)
定义
s
o
l
v
e
(
l
,
r
)
solve(l,r)
solve(l,r)为:对于第
k
k
k个操作,
k
∈
[
l
,
r
]
k∈[l,r]
k∈[l,r],若其为查询操作,则计算
[
l
,
k
−
1
]
[l,k-1]
[l,k−1]中的修改对ans[i]造成的影响。设
m
i
d
=
(
l
+
r
)
/
2
,
s
o
l
v
e
(
l
,
r
)
mid = (l+r)/2,solve(l,r)
mid=(l+r)/2,solve(l,r)的执行步骤如下:
- 递归计算 s o l v e ( l , m i d ) solve(l,mid) solve(l,mid);
- 计算 [ l , m i d ] [l,mid] [l,mid]中所有的修改对 [ m i d + 1 , r ] [mid+1,r] [mid+1,r]中所有查询所造成的影响;
- 递归计算 s o l v e ( m i d + 1 , r ) solve(mid+1,r) solve(mid+1,r);
应用
一.
- 题目链接:https://www.luogu.com.cn/problem/P3372
const int N = 1e5 + 5;
int n , m, cnt;
ll val[N], ans[N], d[N];
struct node {
int op, l, r, id, aid;
ll k;
}qes[N << 1];
void solve (int l ,int r)
{
if (l == r) return;
int mid = (l + r) >> 1;
solve (l, mid);
for (int i = 1 ; i <= n ; i ++) d[i] = 0;
for (int i = l ; i <= r ; i ++)
{
if (qes[i].op == 1 && qes[i].id <= mid)
{
d[qes[i].l] += qes[i].k;
d[qes[i].r + 1] -= qes[i].k;
}
}
for (int i = 1 ; i <= n ; i ++)
d[i] += d[i-1];
for (int i = 1 ; i <= n ; i ++)
d[i] += d[i-1];
for (int i = l ; i <= r ; i ++)
{
if (qes[i].op == 2 && qes[i].id > mid)
ans[qes[i].id] += d[qes[i].r] - d[qes[i].l-1];
}
solve (mid + 1 , r);
}
int main()
{
CLOSE;
cin >> n >> m;
for (int i = 1 ; i <= n ; i ++) {
cin >> val[i];
qes[i].op = 1, qes[i].l = i , qes[i].r = i , qes[i].id = i;
qes[i].k = val[i];
}
for (int i = n + 1 ; i <= n + m ; i ++)
{
int op;
cin >> op;
qes[i].op = op;
qes[i].id = i;
if (op == 1)
cin >> qes[i].l >> qes[i].r >> qes[i].k;
else
cin >> qes[i].l >> qes[i].r;
}
solve (1, n + m);
for (int i = 1 ; i <= n + m ; i ++)
if (qes[i].op == 2)
cout << ans[i] << endl;
}
二.
- 题目链接:https://www.luogu.com.cn/problem/P1908
- 解题思路:将逆序对问题转换为修改和查询操作之后然后进行CDQ分治
const int N = 5e5 + 5;
int n , m, cnt = 0;
struct node {
int op , val, id;
}q[N << 1], q1[N << 1];
int ans[N];
//op == 1 查询
bool cmp (node a, node b)
{
if (a.val == b.val)
return a.op < b.op;
return a.val > b.val;
}
void solve (int l ,int r)
{
if (l == r) return ;
int mid = (l + r) >> 1;
solve (l, mid);
int tot = 0;
sort (q + l , q + r + 1, cmp);
for (int i = l ; i <= r ; i ++)
{
if (q[i].op == 2 && q[i].id <= mid) tot ++;
if (q[i].op == 1 && q[i].id > mid) ans[q[i].id] += tot;
}
for (int i = l ; i <= r ; i ++)
q[i] = q1[i];
solve (mid + 1, r);
}
int main()
{
CLOSE;
cin >> n;
for (int i = 1 ; i <= n ; i ++)
{
int x;
cin >> x;
q[++ cnt].id = cnt;
q[cnt].val = x;
q[cnt].op = 1;
q[++ cnt].id = cnt;
q[cnt].val = x;
q[cnt].op = 2;
}
memcpy (q1, q, sizeof (q));
solve (1, cnt);
ll ANS = 0;
for (int i = 1 ; i <= cnt ; i ++)
ANS += ans[i];
cout << ANS << endl;
}