cdq分治是一种特殊的分治,在有些时候可以代替复杂的数据结构来解决一些问题。
今天刚学了cdq分治,在这里以HDU1166为例(本来是树状数组或者线段树的模板题),来说一下我对cdq的理解。
首先说一下cdq分治的优点,就是比好写(相对于某些数据结构),可以维护一些dp(暂时还不会)
缺点是,要求题目的询问可以转化为离线处理。
下面是一般cdq分治的大致步骤:
1,将所有的操作(询问操作或者查询操作)先按照一个标准排序从左到右依次为[L , R](下面这道题按默认的时间顺序,所以不需要做任何操作)
2,将区间[L, R]分为两部分分别是区间[L, mid],[mid + 1, R],其中mid = (L + R) / 2;
3,先递归处理这两个区间。
4,枚举区间[L,mid]中的修作操作会对区间[mid + 1,R]中查询操作造成的影响。
以HDU为例,因为[L, mid]里面的修改操作先与[mid + 1, R](操作按时间先后默认排序),所以[L, mid]里面的修改操作会影响到[mid + 1, R]里面的查询的操作,然而[mid + 1,R]里面的修改操作不会影响到[L, mid]里面的查询操作,然后,只有当[L, mid]里面的修改操作的修改位置小于[mid + 1, R]里面的查询操作的位置时,才会对这个查询操作造成影响(因为我们是维护的一个前缀和),然后对这个影响进行处理就行。说的比较抽象,下面是HDU1166的代码(这个c++代码不能ac的,要改成c代码能ac,,,)、
#include <bits/stdc++.h>
using namespace std;
const int maxn = 50000 + 10;
const int maxq = 40000 + 10;
int n, q;
int qnum, anum;
int ans[maxn];//保存结果数组
struct query{
int loc;//操作点的位置
int value;
int type;//操作类型, 1为修改操作,2, 3,为询问操作
bool operator < (const query &q) const{
if(loc == q.loc) return type < q.type;
return loc < q.loc;
}
}Query[maxq<<1];
query term[maxq<<1];
void cdq(int L, int R)
{
if(L == R) return;
int mid = (L + R)>>1;
cdq(L, mid);
cdq(mid + 1, R);
int sum = 0;
int ll = L, rr = mid + 1;
int res = 0;
while(ll <= mid && rr <= R)
{
if(Query[ll] < Query[rr])
{
if(Query[ll].type == 1) sum += Query[ll].value;
term[++res] = Query[ll++];
}
else
{
if(Query[rr].type == 2) ans[Query[rr].value] -= sum;
else if(Query[rr].type == 3) ans[Query[rr].value] += sum;
term[++res] = Query[rr++];
}
}
while(ll <= mid)
{
if(Query[ll].type == 1) sum += Query[ll].value;
term[++res] = Query[ll++];
}
while(rr <= R)
{
if(Query[rr].type == 2)
ans[Query[rr].value] -= sum;
else if(Query[rr].type == 3)
ans[Query[rr].value] += sum;
term[++res] = Query[rr++];
}
for(int i = 1; i <= res; i++) Query[L++] = term[i];
}
void init()
{
qnum = 0;
anum = 0;
memset(ans, 0, sizeof(ans));
}
int main()
{
int T;
int Case = 1;
scanf("%d", &T);
while(T--)
{
scanf("%d", &n);
init();
for(int i = 1; i <= n; i++)
{
Query[++qnum].loc = i;
Query[qnum].type = 1;
scanf("%d", &Query[qnum].value);
}
char op[10];
int l, r;
while(~scanf("%s", op))
{
if(op[0] == 'E') break;
if(op[0] == 'Q')
{
scanf("%d%d", &l, &r);
Query[++qnum].loc = l - 1;
Query[qnum].type = 2;
Query[qnum].value = ++anum;
Query[++qnum].loc = r;
Query[qnum].type = 3;
Query[qnum].value = anum;
}
else if(op[0] == 'A')
{
scanf("%d%d", &l, &r);
Query[++qnum].loc = l;
Query[qnum].type = 1;
Query[qnum].value = r;
}
else
{
scanf("%d%d", &l, &r);
Query[++qnum].loc = l;
Query[qnum].type = 1;
Query[qnum].value = -r;
}
}
cdq(1, qnum);
printf("Case %d:\n", Case++);
for(int i = 1; i <= anum; i++)
{
printf("%d\n", ans[i]);
}
}
return 0;
}