CDQ分治笔记
以前一直不会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树,先咕