SPOJ D-query

主席树求区间不同数字个数模板题。

求不同个数就是把数组下标建一颗主席树,如果当前数字已经在前面出现过,那么在当前线段树里把之前出现的区间-1,然后把现在出现的区间+1。

-1是因为当前点和重复数字出现的区间所组成的区间答案要减1,+1相当于现在出现了一个新的数字。

查询的时候查询第R颗线段树的出现L区间的不同数字和即可。

 

#include <bits/stdc++.h>
using namespace std;
const int M = 3e4+7;
int n,q,a[M],tot,pos;
int num[M<<5],L[M<<5],R[M<<5],T[M],flag[1000007];
/*************************zhuxishu***************************/
void build(int l,int r,int &rt){
    rt=++tot;
    num[rt]=0;
    if(l==r) return ;
    int mid=(l+r)>>1;
    build(l,mid,L[rt]);
    build(mid+1,r,R[rt]);
}
void update(int l,int r,int &rt,int pre,int v){
    rt=++tot;
    num[rt]=num[pre]+v;
    if(l==r) return ;
    int mid=(l+r)>>1;
    L[rt]=L[pre];R[rt]=R[pre];
    if(pos<=mid) update(l,mid,L[rt],L[pre],v);
    else update(mid+1,r,R[rt],R[pre],v);
}
int query(int p,int l,int r,int rt){
    if(l==r){
        return num[rt];
    }
    int mid=(l+r)>>1,res=0;
    if(p<=mid){
        res+=num[R[rt]];
        res+=query(p,l,mid,L[rt]);
    }
    else{
        res+=query(p,mid+1,r,R[rt]);
    }
    return res;
}
/*************************solve**************************/
void solve(){
    build(1,n,T[0]);int t;
    for(int i=1;i<=n;i++){
        pos=i;
        if(flag[a[i]]){
            pos=flag[a[i]];
            update(1,n,t,T[i-1],-1);pos=i;
            update(1,n,T[i],t,1);
        }
        else update(1,n,T[i],T[i-1],1);
        flag[a[i]]=i;
    }
    scanf("%d",&q);
    while(q--){
        int x,y;
        scanf("%d%d",&x,&y);
        printf("%d\n",query(x,1,n,T[y]));
    }
}
int main(){
    freopen("1.in","r",stdin);
    freopen("1.out","w",stdout);
    scanf("%d",&n);tot=0;
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    solve();
    return 0;
}
View Code

 

posted @ 2018-08-31 16:29  LMissher  阅读(177)  评论(0)    收藏  举报