CDQ分治正确的入门姿势

来源:互联网 发布:js删除指定class的div 编辑:程序博客网 时间:2024/04/30 09:58

CDQ分治(陈丹琦分治)的思想就是降维

先来看个题:

1.在数轴上(1~1e9),初始化每点为0。两种操作:1 a  b(将点a加b),2 a b(询问区间a—b值的总和)。

很快会想到线段树或树状数组,因为区间过大,所以离线化一下就好了,这里因为前缀和需要,说以我们就使用树状数组。先把操作2才分开记录下询问的id编号,这个询问的答案就是两个前缀和相减,这里我们来深层的挖一下这么做的原因:其实这里有两个维度:位置x和时间t,时间t是按输入顺序自动排序好的,所以我们容易忽视时间t维度,其实时间维度与位置维度是完全一样的,我们不妨按位置x排序(同样把操作2分开,操作1和操作2丢在一起排),按时间t来树状数组,也会得到正确答案。这是为什么呢?因为不管我们是按位置做排序时间做树状数组,还是按时间排序按位置做树状数组,都是得到的两个维度前缀和的相交部分(可能解释得不太到位0.0,请见谅)。

总结上面这题,就是:时间t维度和位置x维度,一个维度排序,另一个维度用树状数组。

此时进入正题,再来看一题:

2.在平面上,(x,y:1~1e9),初始化每点为0。两种操作:1 a b c(将点a,b加c),2 a1 b1 a2 b2(询问矩形区域a1,b1—a2,b2值的总和)。

很快就会想到二维线段树或二维树状数组再加个离散化,但是如果询问次数大于10000,离散化后可能还要开20000*20000的空间,所以最多只能开一维的树状数组,参考上一题的降维解法,这一题有3个维度:时间t 位置x 位置y,我们二分t维度,排序x维度,树状数组y维度,就可以成功降维了。

总结上面这题:通过二分,排序,树状数组降维。

分析上面这题:每个维度是可以前缀思想来处理的 才可以用这个方法!因为只有这样,前面的修改操作才会有且仅有一次地影响到后面的查询操作(可能解释得不太到位0.0,请见谅)。

下面再来看一题:

3.在空间上,(x,y,z:1~1e9),初始化每点为0。两种操作:1 a b c d(将点a,b,cd),2 a1 b1  c1 a2 b2 c2(询问长方体区域a1,b1,c1—a2,b2,c2值的总和)。

参考上面一题降维解法,这一题有4个维度:时间t 位置x 位置y位置z,我们二分t维度,二分x维度,排序y维度,树状数组z维度,就可以成功降维了。


下面是与第3题类似的一题:

http://acm.hdu.edu.cn/showproblem.php?pid=5126 

分析看上面,代码看下面0.0,个人觉得这代码足够清晰,不用加多余注释,哈哈。

#include<cstring>#include<string>#include<iostream>#include<queue>#include<cstdio>#include<algorithm>#include<map>#include<cstdlib>#include<cmath>#include<vector>//#pragma comment(linker, "/STACK:1024000000,1024000000");using namespace std;#define INF 0x3f3f3f3fstruct node{    int x,y,z;    int kind,id;    node(){}    node(int _x,int _y,int _z,int _k,int _id):x(_x),y(_y),z(_z),kind(_k),id(_id){}};vector<node>q,q1,q2;vector<int>mdzz;int ans[500005],c[500005];void init(){    q.clear();    mdzz.clear();    memset(c,0,sizeof c);}bool cmp(node A,node B){    if(A.x==B.x) return A.id<B.id;    return A.x<B.x;}bool cmp1(node A,node B){    if(A.y==B.y) return A.id<B.id;    return A.y<B.y;}int lowbit(int x){    return x&-x;}void update(int x,int val){    for(;x<=mdzz.size();x+=lowbit(x)) c[x]+=val;}int query(int x){    int sum=0;    for(;x>0;x-=lowbit(x)) sum+=c[x];    return sum;}void countstar(){    for(int i=0;i<q2.size();i++)    {        if(q2[i].kind==0) update(q2[i].z,1);        else ans[q2[i].id]+=q2[i].kind*query(q2[i].z);    }    for(int i=0;i<q2.size();i++)    {        if(q2[i].kind==0) update(q2[i].z,-1);    }}void CDQ1(int l,int r){    if(l>=r) return ;    int mid=l+r>>1;    CDQ1(l,mid);    CDQ1(mid+1,r);    q2.clear();    for(int i=l;i<=mid;i++)    {        if(q1[i].kind==0) q2.push_back(q1[i]);    }    for(int i=mid+1;i<=r;i++)    {        if(q1[i].kind!=0) q2.push_back(q1[i]);    }    sort(q2.begin(),q2.end(),cmp1);    countstar();}void CDQ(int l,int r){    if(l>=r) return ;    int mid=l+r>>1;    CDQ(l,mid);    CDQ(mid+1,r);    q1.clear();    for(int i=l;i<=mid;i++)    {        if(q[i].kind==0) q1.push_back(q[i]);    }    for(int i=mid+1;i<=r;i++)    {        if(q[i].kind!=0) q1.push_back(q[i]);    }    sort(q1.begin(),q1.end(),cmp);    CDQ1(0,q1.size()-1);}int main(){    int T;    scanf("%d",&T);    while(T--)    {        int n;        scanf("%d",&n);        init();        for(int i=0;i<n;i++)        {            int op;            scanf("%d",&op);            if(op==1)            {                int x,y,z;                scanf("%d%d%d",&x,&y,&z);                q.push_back(node(x,y,z,0,i));                mdzz.push_back(z);                ans[i]=-1;            }            else            {                int x1,y1,z1,x2,y2,z2;                scanf("%d%d%d%d%d%d",&x1,&y1,&z1,&x2,&y2,&z2);                x1--,y1--,z1--;                q.push_back(node(x1,y1,z1,-1,i));                q.push_back(node(x2,y2,z2,1,i));                q.push_back(node(x1,y2,z2,-1,i));                q.push_back(node(x2,y1,z2,-1,i));                q.push_back(node(x2,y2,z1,-1,i));                q.push_back(node(x2,y1,z1,1,i));                q.push_back(node(x1,y2,z1,1,i));                q.push_back(node(x1,y1,z2,1,i));                mdzz.push_back(z1);                mdzz.push_back(z2);                ans[i]=0;            }        }        sort(mdzz.begin(),mdzz.end());        mdzz.erase(unique(mdzz.begin(),mdzz.end()),mdzz.end());        for(int i=0;i<q.size();i++)        {            q[i].z=(lower_bound(mdzz.begin(),mdzz.end(),q[i].z)-mdzz.begin())+1;        }        CDQ(0,q.size()-1);        for(int i=0;i<n;i++)        {            if(ans[i]!=-1) printf("%d\n",ans[i]);        }    }    return 0;}


原创粉丝点击