[bzoj3262]陌上花开 三维偏序,CDQ分治+树状数组 模版题 (附详细注释)

3262: 陌上花开

Time Limit: 20 Sec  Memory Limit: 256 MB

Description

有n朵花,每朵花有三个属性:花形(s)、颜色(c)、气味(m),用三个整数表示。
现在要对每朵花评级,一朵花的级别是它拥有的美丽能超过的花的数量。
定义一朵花A比另一朵花B要美丽,当且仅Sa>=Sb,Ca>=Cb,Ma>=Mb。
显然,两朵花可能有同样的属性。需要统计出评出每个等级的花的数量。
 

Input

第一行为N,K (1 <= N <= 100,000, 1 <= K <= 200,000 ), 分别表示花的数量和最大属性值。
以下N行,每行三个整数si, ci, mi (1 <= si, ci, mi <= K),表示第i朵花的属性

Output

包含N行,分别表示评级为0...N-1的每级花的数量。

Sample Input

10 3
3 3 3
2 3 3
2 3 1
3 1 1
3 1 2
1 3 1
1 1 2
1 2 2
1 3 2
1 2 1

Sample Output

3
1
3
0
1
0
1
0
0
1
 
不是很了解CDQ的话可以看这篇博客----> https://www.cnblogs.com/mlystdcall/p/6219421.html
咸鱼的我今天刚刚学习了CDQ分治,于是来写一下模版题.对于二维偏序问题,我们可以先按x排序,然后一个一个地向权值树状数组中插入y值,这里的X就相当于时间轴,由于是一个一个加入的,即使后加入的Yj<Yi也不会被统计在Xi的答案中(因为那时还未加入Yj).但是对于这道题来说,多了一维,就不能再单用树状数组维护了,此时就要应用CDQ分治解决一维带来的影响.首先以x为第一键值排序,消除x的影响,再用CDQ分治处理每一段区间,先进行分治再给y排序(就像归并排序)保证了x的顺序不会乱,之后用权值树状数组维护z值(因为这一维的存在无法直接通过归并统计答案),与二维问题相同,这里也是一个一个插入z并统计.最后不要忘记在统计后清理左段区间已经被统计完的z(详见代码).
对CDQ还不是很了解,请多包涵~
 
 
[这里是代码]
 1 #include <cstdio>
 2 #include <algorithm>
 3 #define maxn 1000010
 4 using namespace std;
 5 struct F{
 6     int x,y,z;
 7     int cnt,ans;
 8 }f[maxn];
 9 int n,k,tot;
10 int tr[maxn],num[maxn];
11 int lowbit(int x){
12     return x&-x;
13 }
14 bool cmp(F a,F b){//以x为第一键值,y为第二键值排序
15     if(a.x!=b.x) return a.x<b.x;
16     if(a.y!=b.y) return a.y<b.y;
17     return a.z<b.z;
18 }
19 bool cmp2(F a,F b){//以y为第一键值,z为第二键值排序
20     if(a.y!=b.y) return a.y<b.y;
21     if(a.z!=b.z) return a.z<b.z;
22     return a.x<b.x;
23 }
24 void add(int x,int y){//树状数组单点加
25     while(x<=k){
26         tr[x]+=y;
27         x+=lowbit(x);
28     }
29 }
30 int query(int x){//树状数组区间查询
31     int ans=0;
32     while(x){
33         ans+=tr[x];
34         x-=lowbit(x);
35     }
36     return ans;
37 }
38 void CDQ(int l,int r){//CDQ分治
39     if(l==r){
40         f[l].ans+=f[l].cnt-1;
41         return;
42     }
43     int mid=(l+r)>>1;
44     CDQ(l,mid);//优先递归分治
45     CDQ(mid+1,r);
46     sort(f+l,f+mid+1,cmp2);//然后以y为第二键值排序
47     sort(f+mid+1,f+r+1,cmp2);
48     int j=l;//当前区间左端点
49     for(int i=mid+1;i<=r;i++){//统计当前左半区间对右半区间的影响
50         while(j<=mid&&f[j].y<=f[i].y)//当前左区间中的某点j的x(已经排好序),y,均<=i的
51             add(f[j].z,f[j].cnt),j++;//在树状数组中统计z这一维
52         f[i].ans+=query(f[i].z);//就像二维统计一样,x、y均已有序,因此此时树状数组中query(f[i].z)就应该被统计在f[i]中
53     }
54     for(int i=l;i<j;i++)
55         add(f[i].z,-f[i].cnt);//这些都已经被统计在了右半段中,为了避免重复统计
56 }
57 int main(){
58     scanf("%d%d",&n,&k);
59     for(int i=1;i<=n;i++){
60         scanf("%d%d%d",&f[i].x,&f[i].y,&f[i].z);
61     }
62     sort(f+1,f+n+1,cmp);//先排好x
63     for(int i=1;i<=n;i++){
64         if(f[i].x==f[i-1].x&&f[i].y==f[i-1].y&&f[i-1].z==f[i].z)
65             f[tot].cnt++;//去重
66         else f[++tot]=f[i],f[tot].cnt=1;
67     }
68     CDQ(1,tot);
69     for(int i=1;i<=tot;i++){
70         num[f[i].ans]+=f[i].cnt;
71     }
72     for(int i=0;i<n;i++)
73         printf("%d\n",num[i]);
74     return 0;
75 }

 

posted @ 2018-08-03 20:42  AL76  阅读(145)  评论(0编辑  收藏  举报