SPOJ DQUERY (主席树求区间不同数个数)
题意:找n个数中无修改的区间不同数个数
题解:使用主席树在线做,我们不能使用权值线段树建主席树
我们需要这么想:从左向右添加一到主席树上,添加的是该数字处在的位置
但是如果该数字前面出现过,就在此版本的主席树上的前面出现的位置减一,接着才在此位置上添一
这样查找是按照右区间版本的主席树来找(lef,rig)的数字
因为要将此区间每个不同的数都处在最后出现的位置
/*在线求区间内不同的数的个数:从头到尾添加到线段树(不是权值线段树,是存值的线段树)中 如果此数之前出现过就先减去,接着再加,最后在区间(l,r)中找到root[r]这个历史版本*/ #include<map> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; #define dir(a,b) (a>>b) const int Max=30010; int root[Max],tot,val[Max]; struct node { int lef,rig,sum; }msegtr[Max*40]; map<int,int> mp; void Init() { tot=0; msegtr[0].lef=msegtr[0].rig=msegtr[0].sum=0; root[0]=0; mp.clear(); return; } void Create(int sta,int enn,int &x,int y,int pos,int aad) { msegtr[++tot]=msegtr[y]; msegtr[tot].sum+=aad; x=tot; if(sta==enn) return; int mid=dir(sta+enn,1); if(mid>=pos) Create(sta,mid,msegtr[x].lef,msegtr[y].lef,pos,aad); else Create(mid+1,enn,msegtr[x].rig,msegtr[y].rig,pos,aad); return; } int Query(int sta,int enn,int x,int y)//只有左边有界限 { if(sta>=y) return msegtr[x].sum; int mid=dir(sta+enn,1); if(mid>=y) return Query(sta,mid,msegtr[x].lef,y)+msegtr[msegtr[x].rig].sum; else return Query(mid+1,enn,msegtr[x].rig,y); } int main() { int n,m,temp; int lef,rig; while(~scanf("%d",&n)) { Init(); for(int i=1;i<=n;++i) { scanf("%d",&val[i]); if(!mp.count(val[i]))//直接加 { Create(1,n,root[i],root[i-1],i,1);//注意是在i这个位置加1,不是权值线段树的val[i]位置加1 } else { Create(1,n,temp,root[i-1],mp[val[i]],-1);//先在原位置减去1 Create(1,n,root[i],temp,i,1); } mp[val[i]]=i; } scanf("%d",&m); for(int i=0;i<m;++i) { scanf("%d %d",&lef,&rig); printf("%d\n",Query(1,n,root[rig],lef));//在rig的历史版本上找(lef,rig)的值 } } return 0; }