以前一直不会CDQ……然后经常听到dalao们说“这题直接CDQ啊”“CDQ不就秒了吗”的时候我只能瑟瑟发抖QAQ
CDQ分治
其实CDQ分治就是二分分治,每次将$[l,r]$的问题划分为$[l,mid]$和$[mid+1,r]$的子问题来解决,裸的时间复杂度是$O(nlogn)$。但是cdq的特殊要求是区间左半边的操作不会影响右半边的操作,一般适用于多次询问以及需要维护多个维度关键值的问题。(其实这种题也可以写树套树&KD树,dalao们又把我碾在了地上QAQ)
注意:cdq经常要在中间给数组排序,要使用归并来保证复杂度,直接sort的话或多一个$log$(Orzhjw)
三维偏序问题
三维偏序的著名不用我说了吧。。。几乎是所有人cdq的入门题,会cdq的人应该都写过吧。。。
先从一维偏序问题开始考虑,这就是一个经典的逆序对问题,用树状数组解决;
二维偏序的话可以对第一维排序,保证这一维有序后再对第二维建树状数组维护;
问题变成了三维,排序+树状数组也只能解决两维,还有一维就要用cdq分治(树套树)来搞:
注意到第一维排序后前一半的答案不会影响后一半的答案,于是把两边的区间分别按照第二维排序,然后建个树状数组解决;
(yrx:树套树好写易懂好调肯定写树套树啊)
代码:(BZOJ3262陌上花开 模板题)
1 #include<algorithm>
2 #include<iostream>
3 #include<cstring>
4 #include<cstdio>
5 #include<cmath>
6 #define lb(x) (x&(-x))
7 using namespace std; 8 struct task{ 9 int a,b,c,tot,ans; 10 task(){ 11 tot=0; 12 } 13 }q[100001],s[100001]; 14 int n,m,tot=0,ans[100001],t[500001]; 15 bool cmp(task a,task b){ 16 if(a.a!=b.a)return a.a<b.a; 17 if(a.b!=b.b)return a.b<b.b; 18 if(a.c!=b.c)return a.c<b.c; 19 return false; 20 } 21 bool _cmp(task a,task b){ 22 if(a.b!=b.b)return a.b<b.b; 23 if(a.c!=b.c)return a.c<b.c; 24 if(a.a!=b.a)return a.a<b.a; 25 return false; 26 } 27 void add(int x,int v){ 28 for(;x<=m;x+=lb(x)){ 29 t[x]+=v; 30 } 31 } 32 int sum(int x){ 33 int ret=0; 34 for(;x;x-=lb(x)){ 35 ret+=t[x]; 36 } 37 return ret; 38 } 39 void cdq(int l,int r){ 40 if(l==r){ 41 s[l].ans+=s[l].tot-1; 42 return; 43 } 44 int mid=(l+r)/2; 45 cdq(l,mid); 46 cdq(mid+1,r); 47 sort(s+l,s+mid+1,_cmp); 48 sort(s+mid+1,s+r+1,_cmp); 49 int j=l; 50 for(int i=mid+1;i<=r;i++){ 51 while(j<=mid&&s[j].b<=s[i].b)add(s[j].c,s[j].tot),j++; 52 s[i].ans+=sum(s[i].c); 53 } 54 for(int i=l;i<j;i++)add(s[i].c,-s[i].tot); 55 } 56 int main(){ 57 scanf("%d%d",&n,&m); 58 for(int i=1;i<=n;i++){ 59 scanf("%d%d%d",&q[i].a,&q[i].b,&q[i].c); 60 q[i].ans=1; 61 } 62 sort(q+1,q+n+1,cmp); 63 for(int i=1;i<=n;i++){ 64 if(i!=1&&q[i].a==q[i-1].a&&q[i].b==q[i-1].b&&q[i].c==q[i-1].c)s[tot].tot++; 65 else s[++tot]=q[i],s[tot].tot=1; 66 } 67 cdq(1,tot); 68 sort(s+1,s+tot+1,cmp); 69 for(int i=1;i<=tot;i++)ans[s[i].ans]+=s[i].tot; 70 for(int i=1;i<=n;i++)printf("%d\n",ans[i]); 71 return 0; 72 }
(这个代码多了一个log跑的巨慢无比)
一些刷题记录:
【BZOJ2176】天使玩偶
KD树大法好
1 #include<algorithm>
2 #include<iostream>
3 #include<cstring>
4 #include<cstdio>
5 #include<cmath>
6 #define inf 1000000000
7 using namespace std; 8 struct node{ 9 int d[2],mi[2],mx[2],ls,rs; 10 }t[2000001]; 11 int D,n,m,rt,x,y,op,tot=0,ans; 12 bool cmp(node a,node b){ 13 return a.d[D]!=b.d[D]?a.d[D]<b.d[D]:a.d[D^1]<b.d[D^1]; 14 } 15 void pushup(int u){ 16 int l=t[u].ls,r=t[u].rs; 17 if(l){ 18 t[u].mi[0]=min(t[u].mi[0],t[l].mi[0]); 19 t[u].mi[1]=min(t[u].mi[1],t[l].mi[1]); 20 t[u].mx[0]=max(t[u].mx[0],t[l].mx[0]); 21 t[u].mx[1]=max(t[u].mx[1],t[l].mx[1]); 22 } 23 if(r){ 24 t[u].mi[0]=min(t[u].mi[0],t[r].mi[0]); 25 t[u].mi[1]=min(t[u].mi[1],t[r].mi[1]); 26 t[u].mx[0]=max(t[u].mx[0],t[r].mx[0]); 27 t[u].mx[1]=max(t[u].mx[1],t[r].mx[1]); 28 } 29 } 30 void build(int &u,int l,int r,int d){ 31 D=d; 32 int mid=(l+r)/2; 33 u=mid; 34 nth_element(t+l,t+u+1,t+r+1,cmp); 35 t[u].mi[0]=t[u].mx[0]=t[u].d[0]; 36 t[u].mi[1]=t[u].mx[1]=t[u].d[1]; 37 if(u>l)build(t[u].ls,l,mid-1,D^1); 38 if(u<r)build(t[u].rs,mid+1,r,D^1); 39 pushup(u); 40 } 41 void ins(int k){ 42 int d=0,now=rt; 43 for(;;){ 44 t[now].mi[0]=min(t[now].mi[0],t[k].mi[0]); 45 t[now].mi[1]=min(t[now].mi[1],t[k].mi[1]); 46 t[now].mx[0]=max(t[now].mx[0],t[k].mx[0]); 47 t[now].mx[1]=max(t[now].mx[1],t[k].mx[1]); 48 if(t[k].d[d]>=t[now].d[d]){ 49 if(!t[now].rs){ 50 t[now].rs=k; 51 return; 52 } 53 now=t[now].rs; 54 }else{ 55 if(!t[now].ls){ 56 t[now].ls=k; 57 return; 58 } 59 now=t[now].ls; 60 } 61 d^=1; 62 } 63 } 64 int dis(int u,int x,int y){ 65 int ret=0; 66 if(x<t[u].mi[0])ret+=t[u].mi[0]-x; 67 if(x>t[u].mx[0])ret+=x-t[u].mx[0]; 68 if(y<t[u].mi[1])ret+=t[u].mi[1]-y; 69 if(y>t[u].mx[1])ret+=y-t[u].mx[1]; 70 return ret; 71 } 72 int query(int u,int x,int y){ 73 int l,r,now; 74 now=abs(t[u].d[0]-x)+abs(t[u].d[1]-y); 75 ans=min(ans,now); 76 if(t[u].ls)l=dis(t[u].ls,x,y); 77 else l=inf; 78 if(t[u].rs)r=dis(t[u].rs,x,y); 79 else r=inf; 80 if(l<r){ 81 if(l<ans)query(t[u].ls,x,y); 82 if(r<ans)query(t[u].rs,x,y); 83 }else{ 84 if(r<ans)query(t[u].rs,x,y); 85 if(l<ans)query(t[u].ls,x,y); 86 } 87 } 88 int main(){ 89 scanf("%d%d",&n,&m); 90 for(int i=1;i<=n;i++){ 91 scanf("%d%d",&t[i].d[0],&t[i].d[1]); 92 } 93 build(rt,1,n,0); 94 tot=n; 95 for(int i=1;i<=m;i++){ 96 scanf("%d%d%d",&op,&x,&y); 97 if(op==1){ 98 ++tot; 99 t[tot].mi[0]=t[tot].mx[0]=t[tot].d[0]=x; 100 t[tot].mi[1]=t[tot].mx[1]=t[tot].d[1]=y; 101 ins(tot); 102 }else{ 103 ans=inf; 104 query(rt,x,y); 105 printf("%d\n",ans); 106 } 107 } 108 return 0; 109 }
【BZOJ1176】Mokia
下面一题的加强版,必须要写cdq
1 #include<algorithm>
2 #include<iostream>
3 #include<cstring>
4 #include<cstdio>
5 #include<cmath>
6 #define lb(x) (x&(-x))
7 using namespace std; 8 struct node{ 9 int v,x,y,op,d,id; 10 }q[500001],tmp[500001]; 11 int s,w,x,y,a,b,k,op,cnt=0,tot=0,aans[500001],ans[500001],t[5000001]; 12 bool cmp(node a,node b){ 13 if(a.x!=b.x)return a.x<b.x; 14 if(a.y!=b.y)return a.y<b.y; 15 return a.op<b.op; 16 } 17 void add(int u,int x){ 18 for(;u<=w;u+=lb(u)){ 19 t[u]+=x; 20 } 21 } 22 int query(int u){ 23 int ret=0; 24 for(;u;u-=lb(u)){ 25 ret+=t[u]; 26 } 27 return ret; 28 } 29 void cdq(int l,int r){ 30 if(l==r)return; 31 int mid=(l+r)/2,L=l,R=mid+1; 32 for(int i=l;i<=r;i++){ 33 if(q[i].v<=mid&&!q[i].op)add(q[i].y,q[i].d); 34 if(q[i].v>mid&&q[i].op)ans[q[i].id]+=query(q[i].y)*q[i].d; 35 } 36 for(int i=l;i<=r;i++){ 37 if(q[i].v<=mid&&!q[i].op)add(q[i].y,-q[i].d); 38 } 39 for(int i=l;i<=r;i++){ 40 if(q[i].v<=mid)tmp[L++]=q[i]; 41 else tmp[R++]=q[i]; 42 } 43 for(int i=l;i<=r;i++)q[i]=tmp[i]; 44 cdq(l,mid); 45 cdq(mid+1,r); 46 } 47 int main(){ 48 scanf("%d%d",&s,&w); 49 for(;;){ 50 scanf("%d",&op); 51 if(op==1){ 52 scanf("%d%d%d",&x,&y,&k); 53 q[++cnt]=(node){cnt,x,y,0,k,0}; 54 }else if(op==2){ 55 scanf("%d%d%d%d",&x,&y,&a,&b); 56 tot++; 57 aans[tot]=s*(a-x+1)*(b-y+1); 58 q[++cnt]=(node){cnt,a,b,1,1,tot}; 59 q[++cnt]=(node){cnt,a,y-1,1,-1,tot}; 60 q[++cnt]=(node){cnt,x-1,b,1,-1,tot}; 61 q[++cnt]=(node){cnt,x-1,y-1,1,1,tot}; 62 }else break; 63 } 64 sort(q+1,q+cnt+1,cmp); 65 cdq(1,cnt); 66 for(int i=1;i<=tot;i++)printf("%d\n",ans[i]+aans[i]); 67 return 0; 68 }
【BZOJ2683】简单题
KD树大法好(成功跑到垫底QAQ)
1 #include<algorithm>
2 #include<iostream>
3 #include<cstring>
4 #include<cstdio>
5 #include<cmath>
6 using namespace std; 7 int D; 8 struct kdnode{ 9 int ls,rs,num,v,d[2],mi[2],mx[2]; 10 int &operator [](int x){ 11 return d[x]; 12 } 13 friend bool operator <(kdnode a,kdnode b){ 14 return a[D]<b[D]; 15 } 16 }t[200001],rb[200001],s; 17 int n,op,x1,yy,x2,y2,v,rt=0,tot=0,R=5000,ans=0; 18 bool inside(int x1,int yy,int x2,int y2,int x3,int y3,int x4,int y4){ 19 return x1<=x3&&x2>=x4&&yy<=y3&&y2>=y4; 20 } 21 bool outside(int x1,int yy,int x2,int y2,int x3,int y3,int x4,int y4){ 22 return x1>x4||x2<x3||yy>y4||y2<y3; 23 } 24 void pushup(int u){ 25 int l=t[u].ls,r=t[u].rs; 26 for(int i=0;i<=1;i++){ 27 t[u].mi[i]=t[u].mx[i]=t[u][i]; 28 if(l)t[u].mi[i]=min(t[u].mi[i],t[l].mi[i]); 29 if(l)t[u].mx[i]=max(t[u].mx[i],t[l].mx[i]); 30 if(r)t[u].mi[i]=min(t[u].mi[i],t[r].mi[i]); 31 if(r)t[u].mx[i]=max(t[u].mx[i],t[r].mx[i]); 32 } 33 t[u].num=t[l].num+t[r].num+t[u].v; 34 } 35 int query(int u,int x1,int yy,int x2,int y2){ 36 int ret=0; 37 if(!u)return 0; 38 if(inside(x1,yy,x2,y2,t[u].mi[0],t[u].mi[1],t[u].mx[0],t[u].mx[1]))return t[u].num; 39 if(outside(x1,yy,x2,y2,t[u].mi[0],t[u].mi[1],t[u].mx[0],t[u].mx[1]))return 0; 40 if(inside(x1,yy,x2,y2,t[u][0],t[u][1],t[u][0],t[u][1]))ret+=t[u].v; 41 ret+=query(t[u].ls,x1,yy,x2,y2)+query(t[u].rs,x1,yy,x2,y2); 42 return ret; 43 } 44 void ins(int &u,bool d){ 45 if(!u){ 46 u=++tot; 47 t[u][0]=t[u].mi[0]=t[u].mx[0]=s[0]; 48 t[u][1]=t[u].mi[1]=t[u].mx[1]=s[1]; 49 } 50 if(s[0]==t[u][0]&&s[1]==t[u][1]){ 51 t[u].v+=s.v; 52 t[u].num+=s.v; 53 return; 54 } 55 if(s[d]<t[u][d])ins(t[u].ls,d^1); 56 else ins(t[u].rs,d^1); 57 pushup(u); 58 } 59 int rebuild(int l,int r,bool d){ 60 if(l>r)return 0; 61 int mid=(l+r)/2; 62 D=d; 63 nth_element(rb+l,rb+mid,rb+r+1); 64 t[mid]=rb[mid]; 65 t[mid].ls=rebuild(l,mid-1,d^1); 66 t[mid].rs=rebuild(mid+1,r,d^1); 67 pushup(mid); 68 return mid; 69 } 70 int main(){ 71 scanf("%d",&n); 72 for(;;){ 73 scanf("%d",&op); 74 if(op==1){ 75 scanf("%d%d%d",&x1,&yy,&v); 76 //x1^=ans; 77 //yy^=ans; 78 //v^=ans;
79 s[0]=x1; 80 s[1]=yy; 81 s.num=s.v=v; 82 ins(rt,0); 83 //printf("aa %d\n",tot);
84 if(tot==R){ 85 //printf("zjtywwakioi\n");
86 for(int j=1;j<=tot;j++){ 87 rb[j]=t[j]; 88 } 89 rt=rebuild(1,tot,0); 90 R+=5000; 91 } 92 }else if(op==2){ 93 scanf("%d%d%d%d",&x1,&yy,&x2,&y2); 94 //x1^=ans; 95 //yy^=ans; 96 //x2^=ans; 97 //y2^=ans;
98 printf("%d\n",(ans=query(rt,x1,yy,x2,y2))); 99 }else break; 100 } 101 return 0; 102 }
【BZOJ1492】【NOI2007】Cash
CDQ分治维护凸包+斜率优化DP
1 #include<algorithm>
2 #include<iostream>
3 #include<cstring>
4 #include<cstdio>
5 #include<cmath>
6 #define eps 1e-7
7 #define inf 1e15
8 using namespace std; 9 struct task{ 10 double x,y,a,b,k,r; 11 int v,id; 12 }q[100001],tmp[100001]; 13 double gtk(int a,int b){ 14 if(!b)return -inf; 15 if(fabs(q[a].x-q[b].x)<eps)return inf; 16 return (q[a].y-q[b].y)/(q[a].x-q[b].x); 17 } 18 int n,top,s[100001]; 19 double f[100001]; 20 bool cmp(task a,task b){ 21 return a.k>b.k; 22 } 23 void cdq(int l,int r){ 24 if(l==r){ 25 f[l]=max(f[l],f[l-1]); 26 q[l].y=f[l]/(q[l].a*q[l].r+q[l].b); 27 q[l].x=q[l].r*q[l].y; 28 return; 29 } 30 int mid=(l+r)/2,L=l,R=mid+1,j=1; 31 for(int i=l;i<=r;i++){ 32 if(q[i].id<=mid)tmp[L++]=q[i]; 33 else tmp[R++]=q[i]; 34 } 35 for(int i=l;i<=r;i++)q[i]=tmp[i]; 36 cdq(l,mid); 37 top=0; 38 for(int i=l;i<=mid;i++){ 39 while(top>1&>k(s[top-1],s[top])<gtk(s[top-1],i)+eps)top--; 40 s[++top]=i; 41 } 42 s[++top]=0; 43 for(int i=mid+1;i<=r;i++){ 44 while(j<top&>k(s[j],s[j+1])+eps>q[i].k)j++; 45 f[q[i].id]=max(f[q[i].id],q[s[j]].x*q[i].a+q[s[j]].y*q[i].b); 46 } 47 cdq(mid+1,r); 48 L=l,R=mid+1; 49 for(int i=l;i<=r;i++){ 50 if(((q[L].x<q[R].x||(fabs(q[L].x-q[R].x)<eps&&q[L].y<q[R].y))||R>r)&&L<=mid)tmp[i]=q[L++]; 51 else tmp[i]=q[R++]; 52 } 53 for(int i=l;i<=r;i++)q[i]=tmp[i]; 54 } 55 int main(){ 56 scanf("%d%lf",&n,&f[0]); 57 for(int i=1;i<=n;i++){ 58 scanf("%lf%lf%lf",&q[i].a,&q[i].b,&q[i].r); 59 q[i].k=-q[i].a/q[i].b; 60 q[i].id=i; 61 } 62 sort(q+1,q+n+1,cmp); 63 cdq(1,n); 64 printf("%.2lf",f[n]); 65 return 0; 66 }
【HDU5126】stars
毒瘤题,四维CDQ or 三维KD树,先咕