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 }
View 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,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 }
View Code

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:

猜你喜欢

转载自www.cnblogs.com/OI-zzyy/p/11253707.html
今日推荐