思路:主席树的模板题目,我们倒着插入题目就变成了,查询区间不同数的个数k,然后查询区间第(k+1)/2大。写了一下午,发现初始化写错了,初始化的cnt应该是1,还有数组大小要开36倍左右,我刚开始开的20倍一直tle。
#include <stdio.h>
#include <string.h>
#include <algorithm>;
const int N = 2e5 + 15;
using namespace std;
struct TR{
int l,r,sum;
}tree[N*36];
int rot[N];
int cnt;
//普通读入优化
void read(int &x)
{
int f=1;x=0;char s=getchar();
while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}
x*=f;
}
void build(int l,int r,int &rt,int pos,int val)
{
tree[cnt++]=tree[rt];
rt=cnt-1;
tree[rt].sum+=val;
int mid=(l+r)>>1;
if(l==r) return ;
if(pos<=mid) build(l,mid,tree[rt].l,pos,val);
else build(mid+1,r,tree[rt].r,pos,val);
return ;
}
int query(int L,int R,int rt,int l,int r)
{
if(L<=l&&R>=r) return tree[rt].sum;
int mid=(l+r)>>1;
int ans=0;
if(L<=mid) ans+=query(L,R,tree[rt].l,l,mid);
if(R>mid) ans+=query(L,R,tree[rt].r,mid+1,r);
return ans;
}
int query1(int rt,int l,int r,int k)
{
if(l==r) return l;
int mid=(l+r)>>1;
int ans=tree[tree[rt].l].sum;
if(ans>=k) return query1(tree[rt].l,l,mid,k);
else return query1(tree[rt].r,mid+1,r,k-ans);
}
int a[N];
int ans[N];int pre[N];
void init(int rt)
{
cnt=1;
ans[0]=0;memset(rot,0,sizeof(rot));
tree[rt].l=tree[rt].r=tree[rt].sum=0;memset(pre,0,sizeof(pre));
}
int main()
{
int t;read(t);int cas=1;
while(t--)
{
int n,q;
scanf("%d%d",&n,&q);
for(int i=1;i<=n;i++) read(a[i]);
init(n+1);
for(int i=n;i>=1;i--)
{
if(pre[a[i]])
{
rot[i]=rot[i+1];
build(1,n,rot[i],pre[a[i]],-1);
build(1,n,rot[i],i,1);
}
else
{
rot[i]=rot[i+1];
build(1,n,rot[i],i,1);
}
pre[a[i]]=i;
}
for(int i=1;i<=q;i++)
{
int l,r;read(l);read(r);
l=(l+ans[i-1])%n+1;
r=(r+ans[i-1])%n+1;
if(l>r) swap(l,r);
int k=query(l,r,rot[l],1,n);
k=(k+1)/2;
ans[i]=query1(rot[l],1,n,k);//直接查第l颗树的第(k+1)/2大即可。因为区间[r+1,n]的数肯定都大于等于[l,r]区间的数。
}
printf("Case #%d: ",cas++);
for(int i=1;i<=q;i++)
{
if(i!=q)
printf("%d ",ans[i]);
else printf("%d\n",ans[i]);
}
}
return 0;
}