zoukankan      html  css  js  c++  java
  • [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
    ",num[i]);
    74     return 0;
    75 }
  • 相关阅读:
    广搜 BFS()
    最短路-A
    DFS-C
    codeforces contest
    小技巧
    将博客搬至CSDN
    建树
    codeforces gym102411 Equidistant(图论+乱搞)
    codeforces 1250N wires(简单图论)
    Splay 树
  • 原文地址:https://www.cnblogs.com/al76/p/9416197.html
Copyright © 2011-2022 走看看