题意:n个数,m个询问,每次询问区间[l,r],设[l,r]内不同的数有k个,它们在该区间第一个次出现的位置是p1,p2...pk(p1<p2<pk),回答p(k+1)/2.
思路:主席树查区间不同的数的个数这个就不说了,前面的博客有提到过。问题就在于我们在知道k之后,找p(k+1)/2,难道需要在重新建一棵以权值线段树建的主席树,或者二分去找?
其实并不用,在前面博客查区间不同的数的时候我们是从左往右建的,这样的话,主席树中,相同的数都是保存在当前出现过的最右位置。那反过来,从右往左建的话,相同的数就是保存在当前出现过的最左位置,然后再在这方面找第(k+1)/2大的数即可。
#include<bits/stdc++.h> using namespace std; const int N=1e6+11; struct Tree{ int lson,rson,val; }T[N*32]; int tn,a[N],root[N],vis[N]; int build(int l,int r){ int cur=++tn,mid=(l+r)>>1; T[cur].val=0; T[cur].lson=T[cur].rson=0; if(l==r) return cur; T[cur].lson=build(l,mid); T[cur].rson=build(mid+1,r); return cur; } int addT(int x,int l,int r,int p,int val){ int cur=++tn,mid=(l+r)>>1; T[cur]=T[x]; T[cur].val=T[x].val+val; if(l==r) return cur; if(p<=mid) T[cur].lson=addT(T[x].lson,l,mid,p,val); else T[cur].rson=addT(T[x].rson,mid+1,r,p,val); return cur; } int query(int x,int l,int r,int qr){ if(r<=qr) return T[x].val; int ans=0,mid=(l+r)>>1; ans+=query(T[x].lson,l,mid,qr); if(qr>mid) ans+=query(T[x].rson,mid+1,r,qr); return ans; } int findk(int x,int l,int r,int k){ if(l==r) return l; int mid=(l+r)>>1; int lval=T[T[x].lson].val; if(lval>=k) return findk(T[x].lson,l,mid,k); else return findk(T[x].rson,mid+1,r,k-lval); } int main(){ int t=1,T; scanf("%d",&T); while(T--){ int n,m; scanf("%d%d",&n,&m); for(int i=1;i<=n;i++){ scanf("%d",&a[i]); vis[a[i]]=0; } tn=0; root[n+1]=build(1,n); for(int i=n;i>=0;i--){ int tempr=root[i+1]; if(vis[a[i]]) tempr=addT(tempr,1,n,vis[a[i]],-1); root[i]=addT(tempr,1,n,i,1); vis[a[i]]=i; } int l,r,ans=0; printf("Case #%d:",t++); while(m--){ scanf("%d%d",&l,&r); l=(l+ans)%n+1;r=(r+ans)%n+1; if(l>r) swap(l,r); int pos=query(root[l],1,n,r); pos=(pos+1)/2; ans=findk(root[l],1,n,pos); printf(" %d",ans); } printf(" "); } return 0; }