[bzoj3262]陌上花开 三维偏序,CDQ分治+树状数组 模版题 (附详细注释)
3262: 陌上花开
Time Limit: 20 Sec Memory Limit: 256 MBDescription
有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
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
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 }