BZOJ 3262 陌上花开 树套树 (CDQ分治)

题目大意:有n个三元组(a,b,c)。定义一个三元组A大于另一个三元组B当且仅当A的三个元素都不小于B的三个元素.统计出大于(0~n-1)个三元组的数量。

有三个元素,首先用排序把a弄掉,对于(b,c)两个元素可以看做是平面上的点,利用树套树第一维树状数组,第二维Treap可以解决查找平面上在一个点左下方的点的个数。

当然也可以用CDQ分治来解决,要比树套树快很多(然而我就是喜欢树套树)

#include <cstdio>
#include <algorithm>
#define N 100005
using namespace std;
int n,m;
struct Flower {
    int a,b,c;
    void scan() {scanf("%d%d%d",&a,&b,&c);}
    bool operator < (const Flower& rhs) const {
        if(a!=rhs.a) return a<rhs.a;
        if(b!=rhs.b) return b<rhs.b;
        return c<rhs.c;
    }
}a[N];
int ans[N],sum[N];
struct Node {
    Node *ch[2];
    int s,r,v,cnt;
    Node(int,int);
    Node(){}
    void maintain(){s=ch[0]->s+ch[1]->s+cnt;}
    int cmp(int x){
        if(x==v) return -1;
        return x<v ? 0 : 1;
    }
}*null,*root[N*2];
Node :: Node(int x,int y):r(x),v(y){s=null ? 1 : 0; ch[0]=ch[1]=null; cnt=s;}
void Rotate(Node*& o,int d) {
    Node* k=o->ch[d^1];
    o->ch[d^1]=k->ch[d];
    k->ch[d]=o;
    o->maintain(); k->maintain();
    o=k;
    return ;
}
void Insert(Node*& o,int x) {
    int d=o->cmp(x);
    if(o==null) o=new Node(rand(),x);
    else if(d==-1) o->cnt++ , o->s++;
    else {
        Insert(o->ch[d],x); o->maintain();
        if(o->ch[d]->r > o->r) Rotate(o,d^1);
    }
    return ;
}
int Rank(Node* o,int x) {
    if(o==null) return 0;
    int d=o->cmp(x);
    if(d==-1) return o->ch[0]->s+o->cnt;
    return Rank(o->ch[d],x)+ (d==1 ? o->ch[0]->s+o->cnt : 0);
}
inline int lowbit(int x) {return x&-x;}
void change(int x,int y) {
    for(int i=x;i<=m;i+=lowbit(i)) Insert(root[i],y);
    return ;
}
int query(int x,int y) {
    int tmp=0;
    for(int i=x;i;i-=lowbit(i)) tmp+=Rank(root[i],y);
    return tmp;
}
int main() {
    null=new Node(0,0);
    scanf("%d%d",&n,&m);
    for(int i=0;i<=m;i++) root[i]=null;
    for(int i=1;i<=n;i++) a[i].scan();
    sort(a+1,a+1+n);
    for(int i=1;i<=n;i++) {
        if(a[i].a==a[i+1].a && a[i].b==a[i+1].b && a[i].c==a[i+1].c && i!=n) sum[i+1]+=sum[i]+1;
        else {
            int tmp=query(a[i].b,a[i].c);
            ans[tmp]+=sum[i]+1;
        }
        change(a[i].b,a[i].c);
    }
    for(int i=0;i<n;i++) printf("%d\n",ans[i]);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值