BZOJ 3262 陌上花开

题目

话说这是一道权限题,如果我复制过来,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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值