HDU 4605 Magic Ball Game 树上主席树

本文介绍了一种使用主席树的数据结构来解决特定类型的问题的方法。通过构建两棵主席树,分别记录不同条件下的元素数量,可以高效地进行查询操作。文章详细展示了如何通过递归方式构建和更新主席树,并提供了完整的C++代码实现。

用两颗主席树分别存大小为p的球的个数,还有就是处于右儿子的大小为p的个数即可,然后就是简单主席树。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
using namespace std;
const int maxn=100005;
struct pi{
    int lson;
    int rson;
    int sum;
}pp[maxn*18*2];
int root[2][maxn],tot,a[maxn],b[maxn],pa[maxn];
int ll[maxn],rr[maxn];
void build(int cnt,int l,int r){
    pp[cnt].sum=0;
    if(l==r) return ;
    int mid=(l+r)/2;
    pp[cnt].lson=tot+1;
    tot++;
    build(tot,l,mid);
    pp[cnt].rson=tot+1;
    tot++;
    build(tot,mid+1,r);
}
void merg(int qq,int cnt,int n,int p,int k){
    int le=1,ri=n,mid;
    while(le<=ri){
        pp[cnt]=pp[qq];
        mid=(le+ri)/2;
        pp[cnt].sum+=k;
        if(le==ri) return ;
        if(p<=mid){
            pp[cnt].lson=tot+1;
            tot++;
            cnt=tot;
            qq=pp[qq].lson;
            ri=mid;
        }
        else{
            pp[cnt].rson=tot+1;
            tot++;
            cnt=tot;
            qq=pp[qq].rson;
            le=mid+1;
        }
    }
}
int query(int cnt,int le,int ri,int l,int r){
    if(le>=l&&ri<=r){
        return pp[cnt].sum;
    }
    int s=0;
    int mid=(le+ri)/2;
    if(l<=mid) s+=query(pp[cnt].lson,le,mid,l,r);
    if(r>mid) s+=query(pp[cnt].rson,mid+1,ri,l,r);
    return s;
}
void dfs(int u,int n){
    if(ll[u]==-1) return ;
    root[0][ll[u]]=tot+1;
    tot++;
    merg(root[0][u],tot,n,a[ll[u]],1);
    root[0][rr[u]]=tot+1;
    tot++;
    merg(root[0][u],tot,n,a[rr[u]],1);
    root[1][ll[u]]=tot+1;
    tot++;
    merg(root[1][u],tot,n,a[ll[u]],0);
    root[1][rr[u]]=tot+1;
    tot++;
    merg(root[1][u],tot,n,a[u],1);
    dfs(ll[u],n);
    dfs(rr[u],n);
}
int main()
{
    int i,n,m,t;
    cin>>t;
    while(t--){
        cin>>n;
        for(i=1;i<=n;i++){
            scanf("%d",&a[i]);
            b[i]=a[i];
        }
        sort(b+1,b+1+n);
        for(i=1;i<=n;i++){
            a[i]=(int)(lower_bound(b+1,b+1+n,a[i])-b);
        }
        memset(ll,-1,sizeof(ll));
        memset(rr,-1,sizeof(rr));
        cin>>m;
        pa[1]=0;
        for(i=0;i<m;i++){
            int a,b,c;
            scanf("%d%d%d",&a,&b,&c);
            pa[b]=pa[c]=a;
            ll[a]=b;
            rr[a]=c;
        }
        tot=0;
        root[0][0]=tot;
        build(tot,1,n);
        root[1][0]=tot+1;
        tot++;
        build(tot,1,n);
        root[0][1]=tot+1;
        tot++;
        merg(root[0][0],root[0][1],n,a[1],1);
        root[1][1]=tot+1;
        tot++;
        merg(root[1][0],root[1][1],n,a[1],0);
        dfs(1,n);
        cin>>m;
        for(i=0;i<m;i++){
            int a,bb;
            scanf("%d%d",&a,&bb);
            int p=lower_bound(b+1,b+1+n, bb)-b;
            if(p<=n&&b[p]==bb){
                if(query(root[0][pa[a]],1,n,p,p)){
                    printf("0\n");
                    continue;
                }
            }
            int s=0,s1=0;
            if(p>1){
            s+=3*query(root[0][pa[a]],1,n,1,p-1);
            s1+=query(root[1][a],1,n,1,p-1);
            }
            if(p<=n&&b[p]==bb){
                if(p<n)
                s+=query(root[0][pa[a]],1,n,p+1,n);
            }
            else{
                if(p<=n){
                    s+=query(root[0][pa[a]],1,n,p,n);
                }
            }
            printf("%d %d\n",s1,s);
        }
    }
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值