题目链接:点击查看
题目大意:给出一个长度为 n 的数列 a ,再给出 m 次询问,每次询问给出一个区间 [ l , r ] ,问区间 [ l , r ] 内首次出现的数字的位置的中位数
题目分析:题目可能比较绕,但是涉及到区间内第 k 大的问题不难想到主席树了,现在问题就是思考如何利用首次出现的数字这个条件
如果换种问法,问最后一次出现数字的位置,我们不难联想到主席树解决区间内有多少个本质不同的数字这道题目,这道题目实质上就是维护每个数字最后一次出现的位置,所以回到这个题目来,首次出现的位置,我们可以将整个数列倒过来维护,这样根 root[ i ] 代表的那个权值线段树的意义就是,区间 [ i , n ] 内每个数字首次出现的位置了,对于区间 [ l , r ] ,我们可以在 root[ l ] 这棵权值线段树上,查找区间 [ l , r ] 内有多少个不同的数,再求第 k 大就好了
代码:
#include<iostream>
#include<cstdio>
#include<string>
#include<ctime>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<stack>
#include<climits>
#include<queue>
#include<map>
#include<set>
#include<sstream>
#include<cassert>
using namespace std;
typedef long long LL;
typedef unsigned long long ull;
const int inf=0x3f3f3f3f;
const int N=2e5+100;
struct Node
{
int l,r;
int sum;
}tree[N*50];
int cnt,root[N],ans[N],a[N],pre[N];
void init(int pos)
{
memset(pre,0,sizeof(pre));
root[pos]=0;
tree[pos].l=tree[pos].r=tree[pos].sum=0;
cnt=1;
}
void change(int pos,int &k,int l,int r,int val)
{
tree[cnt++]=tree[k];
k=cnt-1;
tree[k].sum+=val;
if(l==r)
return;
int mid=l+r>>1;
if(pos<=mid)
change(pos,tree[k].l,l,mid,val);
else
change(pos,tree[k].r,mid+1,r,val);
}
int query_sum(int rt,int l,int r,int L,int R)
{
if(R<l||L>r)
return 0;
if(L>=l&&R<=r)
return tree[rt].sum;
int mid=L+R>>1;
return query_sum(tree[rt].l,l,r,L,mid)+query_sum(tree[rt].r,l,r,mid+1,R);
}
int query_kth(int rt,int l,int r,int k)
{
if(l==r)
return l;
int mid=l+r>>1;
if(tree[tree[rt].l].sum>=k)
return query_kth(tree[rt].l,l,mid,k);
else
return query_kth(tree[rt].r,mid+1,r,k-tree[tree[rt].l].sum);
}
int main()
{
#ifndef ONLINE_JUDGE
// freopen("input.txt","r",stdin);
// freopen("output.txt","w",stdout);
#endif
// ios::sync_with_stdio(false);
int w;
cin>>w;
int kase=0;
while(w--)
{
int n,m;
scanf("%d%d",&n,&m);
init(n+1);
for(int i=1;i<=n;i++)
scanf("%d",a+i);
for(int i=n;i>=1;i--)
{
root[i]=root[i+1];
if(pre[a[i]])
change(pre[a[i]],root[i],1,n,-1);
change(i,root[i],1,n,1);
pre[a[i]]=i;
}
for(int i=1;i<=m;i++)
{
int l,r;
scanf("%d%d",&l,&r);
l=(l+ans[i-1])%n+1;
r=(r+ans[i-1])%n+1;
if(l>r)
swap(l,r);
int k=(query_sum(root[l],l,r,1,n)+1)/2;
ans[i]=query_kth(root[l],1,n,k);
}
printf("Case #%d:",++kase);
for(int i=1;i<=m;i++)
printf(" %d",ans[i]);
puts("");
}
return 0;
}