題意:找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; }