主席树-查询区间有多少个不同的数

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<map>
#include<cstring>
using namespace std;
const int MAXN=3e4+5;
const int M=MAXN*100;
int n,q,tot;
int a[MAXN];
int T[MAXN],lson[M],rson[M],c[M];

int build(int l,int r)
{
    int root=tot++;
    c[root]=0;
    if(l!=r)
    {
        int mid=(l+r)>>1;
        lson[root]=build(l,mid);
        rson[root]=build(mid+1,r);
    }
    return root;
}

int update(int root,int pos,int val)
{
    int newroot=tot++,tmp=newroot;
    c[newroot]=c[root]+val;
    int l=1,r=n;
    while(l<r)
    {
        int mid=(l+r)>>1;
        if(pos<=mid)
        {
            lson[newroot]=tot++;
            rson[newroot]=rson[root];
            newroot=lson[newroot];
            root=lson[root];
            r=mid;
        }else
        {
            rson[newroot]=tot++;
            lson[newroot]=lson[root];
            newroot=rson[newroot];
            root=rson[root];
            l=mid+1;
        }
        c[newroot]=c[root]+val;
    }
    return tmp;
}

int query(int root,int pos)
{
    int ret=0;
    int l=1,r=n;
    while(pos<r)
    {
        int mid=(l+r)>>1;
        if(pos<=mid)
        {
            r=mid;
            root=lson[root];
        }else
        {
            ret+=c[lson[root]];
            root=rson[root];
            l=mid+1;
        }
    }
    return ret+c[root];
}

int main()
{
    while(scanf("%d",&n)==1)
    {
        tot=0;
        for(int i=1;i<=n;i++)
            scanf("%d",&a[i]);
        T[n+1]=build(1,n);
        map<int,int> mp;
        for(int i=n;i>=1;i--)
        {
            if(mp.find(a[i])==mp.end())
            {
                T[i]=update(T[i+1],i,1);
            }else
            {
                int tmp=update(T[i+1],mp[a[i]],-1);
                T[i]=update(tmp,i,1);
            }
            mp[a[i]]=i;
        }
        scanf("%d",&q);
        while(q--)
        {
            int l,r;
            scanf("%d%d",&l,&r);
            printf("%d\n",query(T[l],r));
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值