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 } 
公子世无双
原文地址:https://www.cnblogs.com/LMCC1108/p/10737124.html