CDQ 总结
Posted oi-zzyy
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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\n",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\n",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 分治。
但是
Code:
T3:天使玩偶
题干:
题解:
Code:
以上是关于CDQ 总结的主要内容,如果未能解决你的问题,请参考以下文章