BZOJ 3262 陌上花开

来源:互联网 发布:中国战斗力知乎 编辑:程序博客网 时间:2024/06/02 19:36

题目

话说这是一道权限题,如果我复制过来,BZOJ不会打死我吧?hhh

http://www.lydsy.com/JudgeOnline/problem.php?id=3262

题解

大意就是给三维空间中的很多点,一个点p(x,y,z)的级别定义为x0<=x && y0<=y && z0<=z的任意点p0(x0,y0,z0)的数量,求每种级别的点各有多少种。


看过一道二维的这样的题目,当时想的是直接排序然后用BIT(树状数组)秒掉,题解给出了一种排序后用treap维护的方法(不涉及rotate),下面讲一讲这种方法(y为第一关键字,x为第二关键字排序)。

首先treap中的节点存两个值,x坐标和一个sz(等于左儿子的size+1)。将排序后的点一个一个往坐标系里加,在treap中从根节点开始往下走,(设当前点为p)因为排过序所以保证只要当前在p左边的点一定在p下方(这也就是用BIT可以做的原因),设当前在treap里走到的点为p1,答案为ans;
______________1,若p1.x>=p.x则说明p1的左边多了一个点,于是p1.sz++, 往左儿子走(其实我觉得是要往左儿子(以下简称lc)走才加的sz);
______________2,若p1.x<=p.x则说明p在p1右边,所以p1的左子树肯定都在p左边,ans+=p1.sz;
一直往下走,走到p1==NULL就停止,把p放进去,此时p.sz=1;
我一开始也没想明白;
1,p应该在很多点的左边,为什么处理了p1就往左走了?
2,如何做到不重不漏的?
想一想,把treap的根节点看做在坐标系中一根x=k的线,如果你在k右边,那么k左边的点数就等于k.sz,就统计完了;否则,往左走你又会碰到一个点,设为k1,又跟k1比较,一样的,这样不断移动,就能不重不漏统计完所有的点。
想到了什么?这跟BIT在本质上不是差不多吗?如果每次恰好分成两份,不就是一个活生生的BIT吗?但是treap有优点,节点少(后面非常有用),但是treap有缺点,它会被卡成一条链(出题人比较良心没卡我的裸treap)。

二维解决完毕


怎么扩展到三维?排序,对;二维树状数组是不是一眼做?可是开不下,难道要treap套treap?好像没见过这玩意儿(废话我才第一次写树套树),那就BIT套treap。先扔掉一维,像上面一样,这一维没用,那么这就要求我们实现一个功能,单点修改,矩阵求和。(没有删除好像很关键?好像没删除是为了让整体二分过)于是外面BIT维护x轴,内层treap维护某个矩阵,就像原来的BIT的一个节点维护某一段区间一样。然后和二维差不多,外层BIT的循环照样写,只是修改的那一句话要写成treap的update。
这是树套树的第一篇所以写的很长,以后应该就是一句话题解了。

代码

//QWsin#include<cstdio>#include<cstring>#include<iostream>#include<algorithm>using namespace std;const int maxn=100000+10;const int maxv=200000+10;inline int read(){    int ret=0;char ch=getchar();    while(ch<'0'||ch>'9') ch=getchar();    for(;ch>='0'&&ch<='9';ch=getchar()) ret=ret*10+ch-'0';    return ret;}struct Hua{    int a,b,c;    inline void input(){        a=read();b=read();c=read();    }    bool operator < (const Hua &rhs)const{        if(a!=rhs.a) return a<rhs.a;        if(b!=rhs.b) return b<rhs.b;        return c<rhs.c;    }    bool operator == (const Hua &rhs)const{        return a==rhs.a&&b==rhs.b&&c==rhs.c;    }}a[maxn];namespace treap{    struct Node{        int lsz,y;        Node *lc,*rc;           Node(){            lsz=y=0;lc=rc=NULL;        }        Node(int lsz,int y):lsz(lsz),y(y){            lc=rc=NULL;        }    };    Node* root[maxv];    inline int query(int x,int y)    {        Node *cur=root[x];        int count=0;        while(cur!=NULL)        {            if(y >= cur->y) count+=cur->lsz,cur=cur->rc;            else cur=cur->lc;        }        return count;    }    inline void updata(int x,int y)    {        Node *cur=root[x],*fa=NULL;        int t=-1;        while(cur!=NULL)        {            fa=cur;            if(y <= cur->y)cur->lsz++,cur=cur->lc,t=0;            else if(y > cur->y)cur=cur->rc,t=1;        }        if(fa==NULL) root[x]=new Node(1,y);        else if(t) fa->rc=new Node(1,y);        else fa->lc=new Node(1,y);    }}int Maxx=0;namespace BIT{    int C[maxv];    #define lowbit(x) ((x)&(-x))    inline int query(int x,int y)// query and insert    {        int ret=0;        for(;x;x-=lowbit(x)) ret+=treap::query(x,y);        return ret;    }    inline void updata(int x,int y){        for(;x<=Maxx;x+=lowbit(x)) treap::updata(x,y);    }}int ans[maxn],kind[maxn];int main(){    int n,k;cin>>n>>k;    for(int i=1;i<=n;i++) a[i].input(),Maxx=max(Maxx,a[i].c);    sort(a+1,a+n+1);    #define x a[i].c    #define y a[i].b    for(int i=1;i<=n;i++)   {        kind[i]=BIT::query(x,y);        BIT::updata(x,y);    }    for(int i=1,cnt;i<=n;i+=cnt)//先标记kind,方便后面将同位置的点合并,同类的点的等级都跟他们中的最后一个一样    {        for(cnt=0;i+cnt<=n&&a[i]==a[i+cnt];cnt++);        ans[kind[i+cnt-1]]+=cnt;    }    for(int i=0;i<=n-1;i++) printf("%d\n",ans[i]);    return 0;}
1 0