欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

CDQ分治--模板 BZOJ 3262--陌上花开【三维偏序】

程序员文章站 2022-05-14 18:37:45
...

Description

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

题解

这是典型的三维偏序的问题,可以套高级数据结构,当然,更简便的做法是用CDQ分治。

什么是CDQ分治

CDQ分治是陈丹琦大神(Orz%%%)在08提出来的,所以就叫CDQ分治。

我们知道,有一些动态修改的问题,需要各种数据结构,如果没有强制在线的话,就可以用CDQ分治将其转化成静态的问题。

CDQ分治是对操作序列分治:
1.将序列[l,r]分成两半[l,mid]和[mid+1,r];
2.分别处理两半序列中的操作;
3.考虑[l,mid]对[mid+1,r]的影响;

以此题为例

考虑如果是二维偏序我们怎么做?肯定是按照其中一维排序,剩下的交给树状数组,所以三位偏序也一样,先按其中一维排序,然后剩下的交给树套树(不会啊),但是我们不想写树套树,所以就用CDQ分治来处理剩下的两维。

考虑到既然已经按照x排过序了,那么前半段序列中的x一定后半段序列的x,所以我们分别对两段序列排序并不会造成什么影响(两段中的答案都已经各自统计过了),那么我们就将他们分别按照y排序,然后像二维偏序那样用树状数组就可以了(这里要注意,清树状数组时不能用memset,不然会超时,清的时候只能清修改过的,不然复杂度不对)。

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#define maxn 100006
#define maxm 200006
#define lowbit(x) (x&-x)
using namespace std;
inline char nc(){
    static char buf[100000],*i=buf,*j=buf;
    return i==j&&(j=(i=buf)+fread(buf,1,100000,stdin),i==j)?EOF:*i++;
}
inline int _read(){
    char ch=nc();int sum=0;
    while(!(ch>='0'&&ch<='9'))ch=nc();
    while(ch>='0'&&ch<='9')sum=sum*10+ch-48,ch=nc();
    return sum;
}
struct point{
    int x,y,z,p;
    bool operator ==(const point&b)const{return x==b.x&&y==b.y&&z==b.z;}
    bool operator <(const point&b)const{return y<b.y||(y==b.y&&z<b.z);}
}a[maxn],c[maxn];
int n,m,ans[maxn],opt[maxm];
bool cmp(point x,point y){return x.x<y.x||(x.x==y.x&&(x.y<y.y||(x.y==y.y&&x.z<y.z)));}
void put(int x,int y){for(;x<=m;x+=lowbit(x))opt[x]+=y;}
int get(int x){
    int sum=0;
    for(;x;x-=lowbit(x))sum+=opt[x];
    return sum;
}
void clear(int x){for(;x<=m;x+=lowbit(x))opt[x]=0;}
void cdq(int l,int r){
    if(l>=r)return;
    int mid=(l+r)>>1;
    cdq(l,mid);cdq(mid+1,r);
    int i=l;
    for(int j=mid+1;j<=r;j++){
        while(i<=mid&&a[i].y<=a[j].y)put(a[i].z,1),i++;
        a[j].p+=get(a[j].z);
    }
    for(int i=l;i<=mid;i++)clear(a[i].z);
    for(int i=l;i<=r;i++)c[i]=a[i];
    i=l;int j=mid+1;
    for(int k=l;k<=r;k++) if(j>r||((i<=mid&&c[i]<c[j])))a[k]=c[i++];
                                                   else a[k]=c[j++];
}
int main(){
    freopen("cdq.in","r",stdin);
    freopen("cdq.out","w",stdout);
    n=_read();m=_read();
    for(int i=1;i<=n;i++) a[i].x=_read(),a[i].y=_read(),a[i].z=_read();
    sort(a+1,a+1+n,cmp);
    cdq(1,n);
    sort(a+1,a+1+n,cmp);
    int i=1;
    while(i<=n){
        int j=i,Max=0;
        while(j<=n&&a[i]==a[j])Max=max(Max,a[j++].p);
        ans[Max]+=j-i;i=j;
    }
    for(int i=0;i<n;i++)printf("%d\n",ans[i]);
    return 0;
}