• CDQ解决一些三维偏序的问题


      本来几天前就该记录的东西,硬生生被我拖到了现在,太懒了。。。

      在cdq学习时,二维偏序已经解决了,无非就是先sort使第一维有序,然后再用cdq或者数据结构处理第二维。而三维偏序的时候呢,大佬的做法好像有树套树之类的,但是我不会,所以我选择cdq。

      大体的思路很简单,首先也是先使第一维有序,然后cdq把第二维由小到大合并,这时再套个线段树或者树状数组处理第三维。来几题做一下

    BZOJ1935园丁的烦恼

      题目大意:一个花园里有n棵数,然后有m次询问,每次询问一个矩形里有多少颗树。

      把原有的树视为更新操作,然后每个查询分为加上(0,0)到(x1-1,y1-1)范围内的树,减去(0,0)到(x1-1,y2)范围内的树,减去(0,0)到(x2,y1-1)范围内的树,加上(0,0)到(x2,y2)范围内的树,这样就可以处理一个二维前缀和,然后用cdq分治理套树状数组处理y轴

      1 #include<cstdio>
      2 #include<algorithm>
      3 #define lowb(x) (x&(-x))
      4 using namespace std;
      5 const int N=500118,M=500118,Y=10000118,Q=(M<<2)+N;//每个查询分4个 
      6 struct Nop{
      7     int x,y,op,w,id;
      8     friend bool operator < (const Nop &n1,const Nop &n2){
      9         return n1.x==n2.x ? n1.op<n2.op : n1.x<n2.x;
     10     }
     11 }P[Q],temp[Q];
     12 int pn,maxy,ans[M<<1],sumy[Y]={0};
     13 void addp(int x,int y,int op,int w,int id){
     14     P[pn++]=(Nop){x,y,op,w,id};
     15 }
     16 void updata(int y)
     17 {
     18     while(y<=maxy)
     19     {
     20         sumy[y]++;
     21         y+=lowb(y);
     22     }
     23 }
     24 int getsum(int y)
     25 {
     26     int sum=0;
     27     while(y)
     28     {
     29         sum+=sumy[y];
     30         y-=lowb(y);
     31     }
     32     return sum;
     33 }
     34 void clear(int y)
     35 {
     36     while(y<=maxy)
     37     {
     38         if(sumy[y])
     39             sumy[y]=0;
     40         else
     41             break;
     42         y+=lowb(y);
     43     }
     44 }
     45 void cdq(int l,int r)
     46 {
     47     if(l==r)
     48         return ;
     49     int m=(l+r)>>1;
     50     cdq(l,m);
     51     cdq(m+1,r);
     52     int i=l,j=m+1,k=l;
     53     while(i<=m&&j<=r)
     54     {
     55         if(P[i]<P[j])
     56         {
     57             if(P[i].op==0)
     58                 updata(P[i].y);
     59             temp[k++]=P[i++];
     60         }
     61         else
     62         {
     63             if(P[j].op==1)
     64                 ans[P[j].id]+=P[j].w*getsum(P[j].y);
     65             temp[k++]=P[j++];
     66         }
     67     }
     68     while(i<=m)
     69         temp[k++]=P[i++];
     70     while(j<=r)
     71     {
     72         if(P[j].op==1)
     73             ans[P[j].id]+=P[j].w*getsum(P[j].y);
     74         temp[k++]=P[j++];
     75     }
     76     for(i=l;i<=r;i++)
     77     {
     78         clear(P[i].y);
     79         P[i]=temp[i];
     80     }
     81 } 
     82 int main()
     83 {
     84     int n,m,x1,y1,x2,y2;
     85     while(~scanf("%d%d",&n,&m))
     86     {
     87         pn=maxy=0;
     88         for(int i=0;i<n;i++)
     89         {
     90             scanf("%d%d",&x1,&y1);
     91             x1++,y1++;
     92             addp(x1,y1,0,0,0);
     93             maxy=max(maxy,y1);
     94         }
     95         for(int i=0;i<m;i++)
     96         {
     97             ans[i]=0;
     98             scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
     99             x1++,y1++,x2++,y2++;
    100             //拆分成四个查询 
    101             addp(x1-1,y1-1,1,1,i);
    102             addp(x1-1,y2,1,-1,i);
    103             addp(x2,y1-1,1,-1,i);
    104             addp(x2,y2,1,1,i);
    105             maxy=max(maxy,max(y1,y2));
    106         }
    107         cdq(0,pn-1);
    108         for(int i=0;i<m;i++)
    109             printf("%d
    ",ans[i]);
    110     }
    111     return 0;
    112 }
    园丁你为何烦恼呢

      当然,这题很明显是个二维偏序问题,因为树本身都存在了,没有什么更新操作,直接离线树状数组处理就行。

     1 #include<cstdio>
     2 #include<algorithm>
     3 #define lowb(x) (x&(-x))
     4 using namespace std;
     5 const int N=500118,M=500118,Y=10000118,Q=(M<<2)+N;
     6 struct Nop{
     7     int x,y,op,w,id;
     8     friend bool operator < (const Nop &n1,const Nop &n2){
     9         return n1.x==n2.x ? n1.op<n2.op : n1.x<n2.x;
    10     }
    11 }P[Q];
    12 int pn,maxy,ans[M<<1],sumy[Y]={0};
    13 void addp(int x,int y,int op,int w,int id){
    14     P[pn++]=(Nop){x,y,op,w,id};
    15 }
    16 void updata(int y)
    17 {
    18     while(y<=maxy)
    19     {
    20         sumy[y]++;
    21         y+=lowb(y);
    22     }
    23 }
    24 int getsum(int y)
    25 {
    26     int sum=0;
    27     while(y)
    28     {
    29         sum+=sumy[y];
    30         y-=lowb(y);
    31     }
    32     return sum;
    33 }
    34 void clear(int y)
    35 {
    36     while(y<=maxy)
    37     {
    38         if(sumy[y])
    39             sumy[y]=0;
    40         else
    41             break;
    42         y+=lowb(y);
    43     }
    44 }
    45 int main()
    46 {
    47     int n,m,x1,y1,x2,y2;
    48     while(~scanf("%d%d",&n,&m))
    49     {
    50         pn=maxy=0;
    51         for(int i=0;i<n;i++)
    52         {
    53             scanf("%d%d",&x1,&y1);
    54             x1++,y1++;
    55             addp(x1,y1,0,0,0);
    56             maxy=max(maxy,y1);
    57         }
    58         for(int i=0;i<m;i++)
    59         {
    60             ans[i]=0;
    61             scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
    62             x1++,y1++,x2++,y2++;
    63             addp(x1-1,y1-1,1,1,i);
    64             addp(x1-1,y2,1,-1,i);
    65             addp(x2,y1-1,1,-1,i);
    66             addp(x2,y2,1,1,i);
    67             maxy=max(maxy,max(y1,y2));
    68         }
    69         for(int i=1;i<=maxy;i++)
    70             clear(i);
    71         sort(P,P+pn);
    72         for(int i=0;i<pn;i++)
    73         {
    74             if(!P[i].op)
    75                 updata(P[i].y);
    76             else
    77                 ans[P[i].id]+=P[i].w*getsum(P[i].y);
    78         } 
    79         for(int i=0;i<m;i++)
    80             printf("%d
    ",ans[i]);
    81     }
    82     return 0;
    83 }
    园丁不烦恼了

    BZOJ1176Mokia

      题目大意:维护一个W*W的矩阵,初始值均为S.每次操作可以增加某格子的权值,或询问某子矩阵的总权值.修改操作数M<=160000,询问数Q<=10000,W<=2000000.

      S值并没有实际作用忽略就好。这题就不行像上面那题单纯的离线然后树状数组维护就行了,因为是有着一个更新的存在,不同的查询前面的更新是不一样的。所以就是我们大体的思路,把每个操作视为(操作时间,x轴,y轴)这样的带有附加消息的三维有序对,这样第一维已经有序了,我们就只需要把x轴cdq分治处理,然后y轴有树状数组处理,查询也就是维护个二维的前缀和。

      1 #include<cstdio>
      2 #include<algorithm>
      3 #define lowb(x) (x&(-x))
      4 using namespace std;
      5 const int N=160118,M=10118,Y=2001108,Q=(M<<2)+N;
      6 struct Nop{
      7     int x,y,op,w,id;
      8     friend bool operator < (const Nop &n1,const Nop &n2){
      9         return n1.x==n2.x ? n1.op<n2.op : n1.x<n2.x;
     10     }
     11 }P[Q],temp[Q];
     12 int pn,qn,maxy,ans[M]={0},sum[Y]={0};
     13 void addp(int x,int y,int op,int w,int id){
     14     P[pn++]=(Nop){x,y,op,w,id};
     15 }
     16 void updata(int y,int val)
     17 {
     18     while(y<=maxy)
     19     {
     20         sum[y]+=val;
     21         y+=lowb(y);
     22     }
     23 }
     24 int getsum(int y)
     25 {
     26     int ans=0;
     27     while(y)
     28     {
     29         ans+=sum[y];
     30         y-=lowb(y);
     31     }
     32     return ans;
     33 }
     34 void clear(int y)
     35 {
     36     while(y<=maxy)
     37     {
     38         if(sum[y])
     39             sum[y]=0;
     40         else
     41             break;
     42         y+=lowb(y);
     43     }
     44 }
     45 void cdq(int l,int r)
     46 {
     47     if(l==r)
     48         return ;
     49     int m=(l+r)>>1;
     50     cdq(l,m);
     51     cdq(m+1,r);
     52     int i=l,j=m+1,k=l;
     53     while(i<=m&&j<=r)
     54     {
     55         if(P[i]<P[j])
     56         {
     57             if(P[i].op==1)
     58                 updata(P[i].y,P[i].w);
     59             temp[k++]=P[i++]; 
     60         }
     61         else
     62         {
     63             if(P[j].op==2)
     64                 ans[P[j].id]+=P[j].w*getsum(P[j].y);
     65             temp[k++]=P[j++]; 
     66         }
     67     }
     68     while(i<=m)
     69         temp[k++]=P[i++];
     70     while(j<=r)
     71     {
     72         if(P[j].op==2)
     73             ans[P[j].id]+=P[j].w*getsum(P[j].y);
     74         temp[k++]=P[j++]; 
     75     }
     76     for(i=l;i<=r;i++)
     77     {
     78         clear(P[i].y);
     79         P[i]=temp[i];
     80     }
     81 }
     82 int main()
     83 {
     84     int n,op,x1,y1,x2,y2,a;
     85     while(~scanf("%d%d",&n,&n))
     86     {
     87         pn=qn=maxy=0;
     88         while(scanf("%d",&op)&&op!=3)
     89         {
     90             if(op==1)
     91             {
     92                 scanf("%d%d%d",&x1,&y1,&a);
     93                 x1++,y1++;
     94                 addp(x1,y1,op,a,0);
     95                 maxy=max(maxy,y1);
     96             }
     97             else
     98             {
     99                 scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
    100                 x1++,y1++,x2++,y2++;
    101                 addp(x1-1,y1-1,op,1,qn);
    102                 addp(x1-1,y2,op,-1,qn);
    103                 addp(x2,y1-1,op,-1,qn);
    104                 addp(x2,y2,op,1,qn);
    105                 qn++;
    106                 maxy=max(maxy,max(y1,y2));
    107             }
    108         }
    109         cdq(0,pn-1);
    110         for(int i=0;i<qn;i++)
    111         {
    112             printf("%d
    ",ans[i]);
    113             ans[i]=0;
    114         }
    115     }
    116     return 0;
    117 }
    园丁都会了这个肯定会了

    BZOJ3262陌上花开

      题目大意:有n朵花,每朵花有三个属性:花形(s)、颜色(c)、气味(m),用三个整数表示。现在要对每朵花评级,一朵花的级别是它拥有的美丽能超过的花的数量。定义一朵花A比另一朵花B要美丽,当且仅Sa>=Sb,Ca>=Cb,Ma>=Mb。显然,两朵花可能有同样的属性。需要统计出评出每个等级的花的数量。

      这就是个三维偏序的问题了,没有什么更新和查询就是(s,c,m)的三维有序对求顺序对,我们同样是让sort按s由小到大,s相同按c排,c相同按m排的cmp使得第一维s有序,然后cdq分治处理第二维c,树状数组维护第三维m,最后提交,好的,wrong了。因为一个重要的点,,两朵花可能有同样的属性,如果我们不去重的话,那么相同的属性的话,因为它们的顺序不同造成它们的等级不同。所以我们把属性相同的归为一类,然后统计这一类有多少个,然后作为树状数组维护的权值维护就行,有个小细节就是当分到最小的子区间也就是只有一朵花时,它的等级应该还得提升它的权值-1,因为那些和它属性相同的合为一类了,具体的就看代码注释。

      1 #include<cstdio>
      2 #include<algorithm>
      3 #define lowb(x) (x&(-x))
      4 using namespace std;
      5 const int N=100118,K=200118;
      6 struct Flower{
      7     int id,s,c,m,w;
      8     friend bool operator == (const Flower &f1,const Flower &f2){
      9         return f1.s==f2.s&&f1.m==f2.m&&f1.c==f2.c;
     10     }
     11 }F[N],temp[N];
     12 int fn,ans[N]={0},rank[N]={0},summ[K]={0};
     13 //ans[i]保存编号为i的花的等级,也就是属性小于或等于它的数目 
     14 bool cmp(const Flower &f1,const Flower &f2){
     15     return f1.s==f2.s ? (f1.c==f2.c ? f1.m<f2.m : f1.c<f2.c) : f1.s<f2.s;
     16 }
     17 void updata(int m,int val)
     18 {
     19     while(m<=200000)
     20     {
     21         summ[m]+=val;
     22         m+=lowb(m);
     23     }
     24 }
     25 int getsum(int m)
     26 {
     27     int ans=0;
     28     while(m)
     29     {
     30         ans+=summ[m];
     31         m-=lowb(m);
     32     }
     33     return ans;
     34 }
     35 void clear(int m)
     36 {
     37     while(m<=200000)
     38     {
     39         if(summ[m]) 
     40             summ[m]=0;
     41         else
     42             break;
     43         m+=lowb(m);
     44     }
     45 }
     46 void cdq(int l,int r)
     47 {
     48     if(l==r)
     49     {
     50         ans[F[l].id]+=F[l].w-1;//因为相同属性的归到一类了
     51         //所以还得加上F[l].w-1,也就是除它之外,
     52         //其他相同属性的花的数目 
     53         return ;
     54     }
     55     int mid=(l+r)>>1;
     56     cdq(l,mid);
     57     cdq(mid+1,r);
     58     int i=l,j=mid+1,k=l;
     59     while(i<=mid&&j<=r)
     60     {
     61         if(F[i].c<=F[j].c)
     62         {
     63             updata(F[i].m,F[i].w);
     64             temp[k++]=F[i++];
     65         }
     66         else
     67         {
     68             ans[F[j].id]+=getsum(F[j].m);
     69             temp[k++]=F[j++];
     70         }
     71     }
     72     while(i<=mid)
     73         temp[k++]=F[i++];
     74     while(j<=r)
     75     {
     76         ans[F[j].id]+=getsum(F[j].m);
     77         temp[k++]=F[j++];
     78     }
     79     for(i=l;i<=r;i++)
     80     {
     81         clear(F[i].m);
     82         F[i]=temp[i];   
     83     }
     84 }
     85 int main()
     86 {
     87     int n,k;
     88     while(~scanf("%d%d",&n,&k))
     89     {
     90         for(int i=0;i<n;i++)
     91         {
     92             F[i].w=1;
     93             scanf("%d%d%d",&F[i].s,&F[i].c,&F[i].m);
     94         }
     95         sort(F,F+n,cmp);
     96         fn=0;
     97         //去重 
     98         for(int i=0;i<n;i++)
     99         {
    100             if(i&&F[i]==F[i-1])//如果和前一朵花属性相同,归到一类 
    101                 F[fn-1].w++;
    102             else
    103             {
    104                 F[fn]=F[i];
    105                 F[fn].id=fn++;
    106             }
    107         }
    108         //原先n朵花去重就只有fn朵 
    109         cdq(0,fn-1);
    110         for(int i=0;i<fn;i++)
    111         {
    112             rank[ans[F[i].id]]+=F[i].w;
    113             ans[F[i].id]=0;
    114         }
    115         for(int i=0;i<n;i++)
    116         {
    117             printf("%d
    ",rank[i]);
    118             rank[i]=0;
    119         }
    120     }
    121     return 0;
    122 } 
    公子世无双
  • 相关阅读:
    《淘宝技术这十年》读书总结
    广告:互联网公司的纽带
    广告:互联网公司的纽带
    定期存款要及时
    定期存款要及时
    Java实现蓝桥杯VIP算法训练 自行车停放
    Java实现蓝桥杯VIP算法训练 自行车停放
    Java实现蓝桥杯VIP算法训练 自行车停放
    Java实现蓝桥杯VIP算法训练 数组逆序排列
    Java实现蓝桥杯VIP算法训练 数组逆序排列
  • 原文地址:https://www.cnblogs.com/LMCC1108/p/10737124.html
Copyright © 2020-2023  润新知