題目鏈接:點擊查看
題目大意:給出一個長度爲 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;
}