zoukankan      html  css  js  c++  java
  • CDQ 总结

    tip:
      CDQ 分治主要处理三维偏序问题,解题时主要是找出比较量(三个或两个),并找出适当排序顺序(有时不需))。  

      CDQ 分治常常与树状数组搭配,树状数组主要用来统计前缀和(权值前缀和 / 排名)、最值、逆序对。

    实战:

    T1:陌上花开

    题干:

      有 n 朵花,每朵花有三个属性:花形 (s)、颜色 (c)、气味 (m),用三个整数表示。现在要对每朵花评级,一朵花的级别是它拥有的美丽能超过的花的数量。

      定义一朵花 A 比另一朵花 B 要美丽,当且仅当 Sa>=Sb , Ca>=Cb , Ma>=Mb 。

      显然,两朵花可能有同样的属性。需要统计出每个等级的花的数量。

    题解:
      比较明显就可以看出这是一道 CDQ 分治,三个比较量就是 S、C、M,排序顺序对这道题来说没有影响。

      需要注意的是,我们可以发现有可能两朵花的属性完全一样,根据题意来看,这两朵花互相更美丽。。。

      注意一下我们需要求出的是每个等级的花的数量,而不是每朵花的等级。

      在统计答案时需要去重,将完全一样的花合成一朵,只是权值增加了;

      当然,也可以不去重,只是我们需要找出 完全相同的花中答案最大的 作为这种花的等级。

      我们可以将 S sort 一下,将 C 用 CDQ 分治解决,将 M 压进树状数组中来统计。

      在统计答案时(通法),我们直接在结构体中建立一个 ans 变量。因为本题的结果与 ans 更新的顺序没有直接关系,能更新就更新即可

    Code:

     1 #include<cstdio>
     2 #include<cstring>
     3 #include<cstdlib>
     4 #include<algorithm>
     5 #define $ 200010
     6 using namespace std;
     7 int m,n,k,t,ans[$*2],cnt,tr[$*2];
     8 struct tree{
     9     int s,c,m,t,sum,ans;  
    10     friend bool operator != (tree x,tree y){
    11         if(x.s!=y.s) return 1;
    12         if(x.c!=y.c) return 1;
    13         if(x.m!=y.m) return 1;
    14         return 0;
    15     }  
    16     friend bool operator <  (tree x,tree y){
    17         if(x.s!=y.s) return x.s<y.s;
    18         if(x.c!=y.c) return x.c<y.c;
    19         return x.m<y.m;
    20     }
    21 }a[$],pre[$],now[$];
    22 inline void add(int x,int add){
    23     if(x==0) return;
    24     for(register int i=x;i<=k;i+=i&(-i)) tr[i]+=add; 
    25 }
    26 inline int ask(int x,int ans=0){
    27     for(register int i=x;i>=1;i-=i&(-i)) ans+=tr[i];
    28     return ans;
    29 }
    30 inline void CDQ(int l,int r){
    31     if(l==r) return;
    32     int mid=(l+r)>>1;
    33     CDQ(l,mid);  CDQ(mid+1,r);
    34     int left=l,right=mid+1,tip=0;
    35     while(left<=mid&&right<=r){
    36         if(a[left].c<=a[right].c)
    37             add(a[left].m,a[left].sum), now[++tip]=a[left++];
    38         else
    39             a[right].ans+=ask(a[right].m), now[++tip]=a[right++];
    40     }
    41     while(right<=r)
    42         a[right].ans+=ask(a[right].m), now[++tip]=a[right++];
    43     for(register int i=l;i<left;++i) add(a[i].m,0);
    44     while(left<=mid)  now[++tip]=a[left++];
    45     for(register int i=l;i<=r;++i)   a[i]=now[i-l+1];
    46 }
    47 signed main(){
    48     scanf("%d%d",&n,&k);
    49     for(register int i=1;i<=n;++i)
    50         scanf("%d%d%d",&pre[i].s,&pre[i].c,&pre[i].m);
    51     sort(pre+1,pre+n+1);
    52     for(register int i=1,sum=1;i<=n;++i,++sum)
    53         if(pre[i+1]!=pre[i]) a[++cnt]=pre[i], a[cnt].sum=sum, sum=0;
    54     CDQ(1,cnt);
    55     for(register int i=1;i<=cnt;++i) ans[a[i].ans+a[i].sum]+=a[i].sum;
    56     for(register int i=1;i<=n;++i)   printf("%d
    ",ans[i]);
    57 }
    有合并
     1 #include<cstdio>
     2 #include<cstring>
     3 #include<cstdlib>
     4 #include<algorithm>
     5 #define $ 200010
     6 using namespace std;
     7 int m,n,k,t,ans[$*2],cnt,tr[$*2];
     8 struct tree{
     9     int s,c,m,t,ans;  
    10     friend bool operator == (tree x,tree y){
    11         if(x.s!=y.s) return 0;
    12         if(x.c!=y.c) return 0;
    13         if(x.m!=y.m) return 0;
    14         return 1;
    15     }  
    16     friend bool operator <  (tree x,tree y){
    17         if(x.s!=y.s) return x.s<y.s;
    18         if(x.c!=y.c) return x.c<y.c;
    19         if(x.m!=y.m) return x.m<y.m;
    20         return x.ans<y.ans;
    21     }
    22 }a[$],pre[$],now[$];
    23 inline void add(int x,int add){
    24     if(x==0) return;
    25     for(register int i=x;i<=k;i+=i&(-i)) tr[i]+=add; 
    26 }
    27 inline int ask(int x,int ans=0){
    28     for(register int i=x;i>=1;i-=i&(-i)) ans+=tr[i];
    29     return ans;
    30 }
    31 inline void CDQ(int l,int r){
    32     if(l==r) return;
    33     int mid=(l+r)>>1;
    34     CDQ(l,mid);  CDQ(mid+1,r);
    35     int left=l,right=mid+1,tip=0;
    36     while(left<=mid&&right<=r){
    37         if(a[left].c<=a[right].c)
    38             add(a[left].m,1), now[++tip]=a[left++];
    39         else
    40             a[right].ans+=ask(a[right].m), now[++tip]=a[right++];
    41     }
    42     while(right<=r)
    43         a[right].ans+=ask(a[right].m), now[++tip]=a[right++];
    44     for(register int i=l;i<left;++i) add(a[i].m,0);
    45     while(left<=mid)  now[++tip]=a[left++];
    46     for(register int i=l;i<=r;++i)   a[i]=now[i-l+1];
    47 }
    48 signed main(){
    49     scanf("%d%d",&n,&k);
    50     for(register int i=1;i<=n;++i)
    51         scanf("%d%d%d",&a[i].s,&a[i].c,&a[i].m);
    52     sort(a+1,a+n+1);  CDQ(1,cnt);  sort(a+1,a+n+1);
    53     int tip=a[cnt].ans;
    54     for(register int i=cnt;i>=1;--i){
    55         if(a[i]==a[i-1]) ans[tip]++; 
    56         else tip=
    57     }
    58     for(register int i=1;i<=n;++i)   printf("%d
    ",ans[i]);
    59 }
    无合并

     

    T2:Mokia / 简单题

    题干:

      维护一个 W * W 的矩阵,初始值均为 S .每次操作可以增加某格子的权值,或询问某子矩阵的总权值.修改操作数 M<=160000,询问数 Q<=10000 , W<=2000000.
      第一行两个整数 , S , W ;其中 S 为矩阵初始值; W 为矩阵大小
      接下来每行为一下三种输入之一(不包含引号):
      "1 x y a"
      "2 x1 y1 x2 y2"
      "3"
      输入 1 :你需要把 (x,y) (第 x 行第 y 列)的格子权值增加 a
      输入 2 :你需要求出以左下角为 (x1,y1) ,右上角为 (x2,y2) 的矩阵内所有格子的权值和,并输出
      输入 3 :表示输入结束

    题解:

      看到 w=2000000 ,数组模拟不优秀,空间、时间复杂度都不行。

      我们可以发现,每种操作都与时间有关,结果的更新也与时间有关;同时操作又与坐标有关。发现这几个变量,我们就可以想到用 CDQ 分治。

      但是‘2’操作有 4 个坐标,用哪两个呢?其实我们不难看出只有最外面的那个坐标起决定作用,因为修改操作只有在访问区域内才可能做贡献

      我们可以利用单步容斥的方法,将一个查询操作分成 4 个查询操作,时间戳一样,还是按照原来的排序顺序(t > x > y)排序即可。

      我们发现, $ans$ 的结果与更新它的时间没有关系,我们完全可以将其作为一个二维偏序问题来解决。

    Code:

     1 #include<cstdio>
     2 #include<cstring>
     3 #include<cstdlib>
     4 #include<algorithm>
     5 #define $ 800010
     6 using namespace std;
     7 int m,n,k,t,s,w,cnt,ans[$],tr[$*3],x,y,xx,yy,derta,opt,num;
     8 struct tree{
     9     int opt,x,y,val,t;
    10     friend bool operator < (tree x,tree y){
    11         if(x.x!=y.x) return x.x<y.x;
    12         if(x.y!=y.y) return x.y<y.y;
    13         return x.opt<y.opt;
    14     }
    15 }a[$],now[$];
    16 inline void add(int x,int add){
    17     if(x==0) return;
    18     if(add==0){
    19         for(register int i=x;i<=w;i+=i&(-i)) tr[i]=0;
    20         return;
    21     }
    22     for(register int i=x;i<=w;i+=i&(-i)) tr[i]+=add;
    23 }
    24 inline int ask(int x,int ans=0){
    25     for(register int i=x;i>=1;i-=i&(-i)) ans+=tr[i];
    26     return ans;
    27 }
    28 inline void CDQ(int l,int r){
    29     if(l==r) return ;
    30     int mid=(l+r)>>1,tip=0,left=l,right=mid+1;
    31     CDQ(l,mid);  CDQ(mid+1,r);
    32     while(left<=mid&&right<=r){
    33         if(a[left].x<=a[right].x){
    34             if(a[left].opt==0) add(a[left].y,a[left].val); 
    35             now[++tip]=a[left]; ++left; 
    36         }
    37         else{
    38             if(a[right].opt==1) 
    39                 ans[a[right].t]+=ask(a[right].y)*a[right].val;
    40             now[++tip]=a[right]; ++right;
    41         }
    42     }
    43     while(left<=mid) now[++tip]=a[left], ++left;
    44     while(right<=r){
    45         if(a[right].opt) ans[a[right].t]+=ask(a[right].y)*a[right].val;
    46         now[++tip]=a[right]; ++right;
    47     }
    48     for(register int i=l;i<=mid;++i) add(a[i].y,0);
    49     for(register int i=l;i<=r;++i) a[i]=now[i-l+1];
    50 }
    51 signed main(){    
    52     scanf("%d%d",&s,&w);
    53     while(~scanf("%d",&opt)){
    54         if(opt==1){
    55             scanf("%d%d%d",&x,&y,&derta);
    56             a[++cnt]=(tree){    0,x,y,derta,0    };
    57         }
    58         if(opt==2){
    59             scanf("%d%d%d%d",&x,&y,&xx,&yy); ++num;
    60             ans[num]=(yy-y+1)*(xx-x+1)*s;
    61             a[++cnt]=(tree){    1,xx,yy,1,num    };
    62             a[++cnt]=(tree){    1,x-1,y-1,1,num    };
    63             a[++cnt]=(tree){    1,x-1,yy,-1,num    };
    64             a[++cnt]=(tree){    1,xx,y-1,-1,num    };
    65         }
    66         if(opt==3) break;
    67     }
    68     CDQ(1,cnt);
    69     for(register int i=1;i<=num;++i)  printf("%d
    ",ans[i]);
    70 }
    排序顺序为 x > y > opt
     1 #include<cstdio>
     2 #include<algorithm>
     3 #include<cstring>
     4 #include<cstdlib>
     5 #define $ 2200000
     6 #define int long long
     7 using namespace std;
     8 struct tree {    int t,x,y,opt,f,val;    }a[$],now[$];
     9 int s,w,tot,opt,up,tr[$*8];
    10 inline bool cmp(const tree &a,const tree &b){
    11     if(a.t!=b.t)  return a.t<b.t;
    12     if(a.x!=b.x)  return a.x<b.x;
    13     return a.y<b.y;
    14 }
    15 inline void add(int x,int add){
    16     if(add==0){
    17         for(register int i=x;i<=w;i+=i&(-i))  tr[i]=0;
    18         return;
    19     }
    20     for(register int i=x;i<=w;i+=i&(-i))  tr[i]+=add;
    21 }
    22 inline int query(int x,int ans=0){
    23     for(register int i=x;i>=1;i-=i&(-i))  ans+=tr[i];
    24     return ans;
    25 }
    26 inline void CDQ(int l,int r){
    27     if(l==r)return;
    28     int mid=(l+r)>>1,left=l,right=mid+1,tip=-1;
    29     CDQ(l,mid),CDQ(mid+1,r);
    30     while(left<=mid&&right<=r){
    31         if(a[left].x<=a[right].x){
    32             if(!a[left].opt) add(a[left].y,a[left].val);
    33             now[++tip]=a[left++];
    34         }else{
    35             if(a[right].opt) a[right].f+=query(a[right].y);
    36             now[++tip]=a[right++];
    37         }
    38     }
    39     while(right<=r){
    40         if(a[right].opt) a[right].f+=query(a[right].y);
    41         now[++tip]=a[right++];
    42     }
    43     for(register int i=l;i<left;++i) add(a[i].y,0);
    44     while(left<=mid)  now[++tip]=a[left++];
    45     for(register int i=l;i<=r;++i)   a[i]=now[i-l];
    46 }
    47 signed main(){
    48     scanf("%lld%lld",&s,&w); ++w;
    49     for(register int i=1,x,y,z,xx,yy;;++i){
    50         scanf("%lld",&opt);
    51         if(opt==1){
    52             scanf("%lld%lld%lld",&x,&y,&z);
    53             a[++tot]=(tree){i,x+1,y+1,0,0,z};
    54         }
    55         if(opt==2){
    56             scanf("%lld%lld%lld%lld",&x,&y,&xx,&yy);
    57             a[++tot]=(tree){    i,xx+1,yy+1,1,0,0    };
    58             a[++tot]=(tree){    i,x,yy+1,-1,0,0    };
    59             a[++tot]=(tree){    i,xx+1,y,-1,0,0    };
    60             a[++tot]=(tree){    i,x,y,1,0,0    };
    61         }
    62         if(opt==3) break;
    63     }
    64     sort(a+1,a+1+tot,cmp);  CDQ(1,tot);
    65     sort(a+1,a+1+tot,cmp);
    66     for(register int i=1;i<=tot;i+=4){
    67         if(!a[i].opt) continue;
    68         int ans=0;
    69         for(register int j=1;j<=4;++j)
    70             ans+=a[i+j-1].opt*(a[i+j-1].f+a[i+j-1].x*a[i+j-1].y*s);
    71         printf("%lld
    ",ans);
    72     }
    73 }
    排序顺序为 t > x > y

    T3:天使玩偶

    题干:

    题解:

      注意曼哈顿距离是有绝对值的,我们不可以简单地将 $x+y$ 压入树状数组来比较,只有这个点在目标节点的左下方才可以进行比较

      我们就可以进行 4 次 CDQ,每次都变换一下每个点的坐标,就相当于是转动了坐标系,分 4 次将答案更新完成。

      我们又可以发现,更新答案与时间无相互限制,可以不保证 t 的单调。

    Code:

     1 #include<cstdio>
     2 #include<cstring>
     3 #include<cstdlib>
     4 #include<algorithm>
     5 #define $ 1001000
     6 #define inf 0x3f3f3f3
     7 #define getchar() (xS==xT&&(xT=(xS=xB)+fread(xB,1,1<<15,stdin),xS==xT)?0:*xS++)
     8 using namespace std;
     9 int m,n,k,t,cnt,num,ans[$],tr[$*2],maxx;
    10 struct tree{    int opt,x,y;    }a[$],now[$],pre[$];
    11 char xB[(1<<15)+10],*xS=xB,*xT=xB;
    12 inline int read(int sum=0){
    13     register char ch=getchar();
    14     for(sum=0;ch<'0'||ch>'9';ch=getchar());
    15     for(;ch>='0'&&ch<='9';sum=(sum<<1)+(sum<<3)+ch-'0',ch=getchar());
    16     return sum;
    17 }
    18 inline int min(int x,int y){    return x<y?x:y;    }
    19 inline int max(int x,int y){    return x>y?x:y;    }
    20 inline void add(int x,int add){
    21     if(x==0) return;
    22     if(add==-inf){
    23         for(register int i=x;i<=maxx;i+=i&(-i)) tr[i]=0;
    24         return;
    25     }
    26     for(register int i=x;i<=maxx;i+=i&(-i)) tr[i]=max(tr[i],add);
    27 }
    28 inline int ask(int x,int ans=0){
    29     for(register int i=x;i>=1;i-=i&(-i)) ans=max(ans,tr[i]);
    30     return (ans==0)?-inf:ans;
    31 } 
    32 inline void CDQ(int l,int r){
    33     if(l==r) return;
    34     int mid=(l+r)>>1,left=l,right=mid+1,tip=0;
    35     CDQ(l,mid);   CDQ(mid+1,r);
    36     while(left<=mid&&right<=r){
    37         if(a[left].x<=a[right].x){
    38             if(!a[left].opt)  add(a[left].y,a[left].x+a[left].y);
    39             now[++tip]=a[left]; ++left;
    40         }
    41         else{
    42             if(a[right].opt) 
    43                 ans[a[right].opt]=min(ans[a[right].opt],a[right].x+a[right].y-ask(a[right].y));
    44             now[++tip]=a[right]; ++right;
    45         }
    46     }
    47     while(right<=r){
    48         if(a[right].opt)     
    49             ans[a[right].opt]=min(ans[a[right].opt],a[right].x+a[right].y-ask(a[right].y));
    50         now[++tip]=a[right], ++right;
    51     }
    52     for(register int i=l;i<left;++i) add(a[i].y,-inf);
    53     while(left<=mid) now[++tip]=a[left], ++left;
    54     for(register int i=l;i<=r;++i)   a[i]=now[i-l+1];
    55 }
    56 signed main(){
    57     memset(ans,63,sizeof(ans));
    58     scanf("%d%d",&n,&m);
    59     for(register int i=1,x,y;i<=n;++i){
    60         x=read(), y=read();
    61         pre[++cnt]=(tree){    0,x+1,y+1    };
    62         maxx=max(maxx,x+y);
    63     }
    64     for(register int i=1,x,y,opt;i<=m;++i){
    65         opt=read(), x=read(), y=read();
    66         if(opt==2) opt=++num;
    67         else opt=0;
    68         pre[++cnt]=(tree){    opt,x+1,y+1    };
    69         maxx=max(maxx,x+y);
    70     } 
    71     maxx+=100;
    72     for(register int i=1;i<=cnt;++i) a[i]=(tree){    pre[i].opt,pre[i].x,pre[i].y    };
    73     CDQ(1,cnt);
    74     for(register int i=1;i<=cnt;++i) a[i]=(tree){    pre[i].opt,maxx-pre[i].x,pre[i].y    };
    75     CDQ(1,cnt); 
    76     for(register int i=1;i<=cnt;++i) a[i]=(tree){    pre[i].opt,pre[i].x,maxx-pre[i].y    };
    77     CDQ(1,cnt); 
    78     for(register int i=1;i<=cnt;++i) a[i]=(tree){    pre[i].opt,maxx-pre[i].x,maxx-pre[i].y    };
    79     CDQ(1,cnt); 
    80     for(register int i=1;i<=num;++i) printf("%d
    ",ans[i]);
    81 }
    x > y > opt

    T4:动态逆序对

      对于序列 A ,它的逆序对数定义为满足 i A j 的数对 (i,j) 的个数。给 1 到 n 的一个排列,按照某种顺序依次删除 m 个元素,你的任务是在每次删除一个元素之前统计整个序列的逆序对数。

      ‘按照顺序删除一个数’可以转化为‘按照顺序添加一个数’;相应地,‘删除元素之前统计’就是‘添加元素之后统计’,这就符合我们平时做题时的按序添加的 CDQ 分治问题。

      发现时间与答案统计有关,我们就还是三维排序即可。

    Code:

     1 #include<cstdio>
     2 #include<cstring>
     3 #include<algorithm>
     4 #define $ 600100
     5 #define int long long
     6 using namespace std;
     7 int m,n,k,t,cnt,mrk[$],ans[$/2],maxx,tr[$*10];
     8 struct tree{
     9     int t,id,w,ans;
    10     friend bool operator < (tree x,tree y){
    11         if(x.t!=y.t)   return x.t<y.t;
    12         if(x.id!=y.id) return x.id<y.id;
    13         return x.w<y.w;
    14     }
    15 }a[$],now[$],pre[$];
    16 inline int max(int x,int y){    return x>y?x:y;    }
    17 inline void add(int x,int add){
    18     if(x==0) return ;
    19     for(register int i=x;i<=maxx;i+=i&(-i)) tr[i]+=add;
    20 }
    21 inline int ask(int x,int ans=0){
    22     for(register int i=x;i>=1;i-=i&(-i)) ans+=tr[i];
    23     return ans;
    24 }
    25 inline void CDQ(int l,int r){
    26     if(l==r) return;
    27     int mid=(l+r)>>1,left=l,right=mid+1,tip=-1;
    28     CDQ(l,mid);  CDQ(mid+1,r);
    29     while(left<=mid&&right<=r){
    30         if(a[left].id<a[right].id)
    31             add(a[left].w,1), now[++tip]=a[left++];
    32         else
    33             ans[a[right].t]+=ask(a[right].w), now[++tip]=a[right++];
    34     }
    35     while(right<=r)
    36         ans[a[right].t]+=ask(a[right].w), now[++tip]=a[right++];
    37     for(register int i=l;i<left;++i) add(a[i].w,-1);
    38     while(left<=mid) now[++tip]=a[left++];
    39     for(register int i=l;i<=r;++i) a[i]=now[i-l];
    40 }
    41 signed main(){
    42     scanf("%lld%lld",&n,&m);  cnt=m;
    43     for(register int i=1;i<=n;++i)
    44         scanf("%lld",&a[i].w), a[i].id=i, maxx=max(maxx,a[i].w);
    45     for(register int i=1,x;i<=m;++i) scanf("%lld",&x), mrk[x]=i;
    46     for(register int i=1;i<=n;++i) if(mrk[i]==0) mrk[i]=++cnt;
    47     for(register int i=1;i<=n;++i) a[i].t=n-mrk[a[i].w]+1;
    48     for(register int i=1;i<=n;++i) a[i].w=maxx-a[i].w+1;
    49     sort(a+1,a+n+1); CDQ(1,n);
    50     for(register int i=1;i<=n;++i) a[i].w=maxx-a[i].w+1, a[i].id=n-a[i].id+1;
    51     sort(a+1,a+n+1); CDQ(1,n);
    52     for(register int i=1;i<=n;++i) ans[i]+=ans[i-1];
    53     for(register int i=n;i>=n-m+1;--i) printf("%lld
    ",ans[i]);
    54 }
    t > x > y
    越努力 越幸运
  • 相关阅读:
    同台电脑 多Git账号同时使用
    netty对http协议解析原理解析(转载)
    Netty 线程模型与Reactor 模式
    增量/存量数据按时间维度分组
    网易技术分享:Nginx缓存引发的跨域惨案
    全面剖析Redis Cluster原理和应用
    聊聊阿里社招面试,谈谈“野生”Java程序员学习的道路
    美团点评基于 Flink 的实时数仓建设实践
    美团技术分享:大众点评App的短视频耗电量优化实战
    美团技术分享:美团深度学习系统的工程实践
  • 原文地址:https://www.cnblogs.com/OI-zzyy/p/11253707.html
Copyright © 2011-2022 走看看