SPOJ - DQUERY (主席树求区间不同数的个数)

题目链接:https://vjudge.net/problem/SPOJ-DQUERY

题目大意:给定一个含有n个数的序列,有q个询问,每次询问区间[l,r]中不同数的个数。

解题思路:从左向右一个一个将该数字处在的位置添加到主席树中

如果该数字前面未出现过,则在此版本的线段树中的该条链加1,如果该数字已经出现过了,则在此版本线段树的上次出现位置减1,再在此版本线段树的该位置加1,这样就能保证区间不重复计算。

代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=30005;
struct node{
    int l,r,sum;
}T[maxn*40];
int n,q,a[maxn],cnt,root[maxn],pos[1000005];
void update(int &now,int pre,int val,int l,int r,int pos){
    T[++cnt]=T[pre],T[cnt].sum+=val,now=cnt;
    if(l==r) return;
    int mid=l+r>>1;
    if(pos<=mid) update(T[now].l,T[pre].l,val,l,mid,pos);
    else update(T[now].r,T[pre].r,val,mid+1,r,pos);
}
int query(int L,int R,int l,int r,int rt){
    if(L<=l&&R>=r) return T[rt].sum;
    int ans=0,mid=l+r>>1;
    if(L<=mid) ans+=query(L,R,l,mid,T[rt].l);
    if(R>mid) ans+=query(L,R,mid+1,r,T[rt].r);
    return ans;
}
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    for(int i=1;i<=n;i++){
        if(pos[a[i]]){
            update(root[i],root[i-1],-1,1,n,pos[a[i]]);
            update(root[i],root[i],1,1,n,i);
        }else update(root[i],root[i-1],1,1,n,i);
        pos[a[i]]=i;
    } 
    scanf("%d",&q);
    while(q--){
        int l,r;
        scanf("%d%d",&l,&r);
        printf("%d\n",query(l,r,1,n,root[r]));
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/zjl192628928/p/11257876.html
今日推荐