Mr. Frog has an integer sequence of length n, which can be denoted as a1,a2,⋯,ana1,a2,⋯,anThere are m queries.
In the i-th query, you are given two integers lili and riri. Consider the subsequence ali,ali+1,ali+2,⋯,ariali,ali+1,ali+2,⋯,ari.
We can denote the positions(the positions according to the original sequence) where an integer appears first in this subsequence as p(i)1,p(i)2,⋯,p(i)kip1(i),p2(i),⋯,pki(i) (in ascending order, i.e.,p(i)1<p(i)2<⋯<p(i)kip1(i)<p2(i)<⋯<pki(i)).
Note that kiki is the number of different integers in this subsequence. You should output p(i)⌈ki2⌉p⌈ki2⌉(i)for the i-th query.
Input
In the first line of input, there is an integer T (T≤2T≤2) denoting the number of test cases.
Each test case starts with two integers n (n≤2×105n≤2×105) and m (m≤2×105m≤2×105). There are n integers in the next line, which indicate the integers in the sequence(i.e., a1,a2,⋯,an,0≤ai≤2×105a1,a2,⋯,an,0≤ai≤2×105).
There are two integers lili and riri in the following m lines.
However, Mr. Frog thought that this problem was too young too simple so he became angry. He modified each query to l‘i,r‘i(1≤l‘i≤n,1≤r‘i≤n)li‘,ri‘(1≤li‘≤n,1≤ri‘≤n). As a result, the problem became more exciting.
We can denote the answers as ans1,ans2,⋯,ansmans1,ans2,⋯,ansm. Note that for each test case ans0=0ans0=0.
You can get the correct input li,rili,ri from what you read (we denote them as l‘i,r‘ili‘,ri‘)by the following formula:
li=min{(l‘i+ansi−1) mod n+1,(r‘i+ansi−1) mod n+1}li=min{(li‘+ansi−1) mod n+1,(ri‘+ansi−1) mod n+1}
ri=max{(l‘i+ansi−1) mod n+1,(r‘i+ansi−1) mod n+1}ri=max{(li‘+ansi−1) mod n+1,(ri‘+ansi−1) mod n+1}
Output
You should output one single line for each test case.
For each test case, output one line “Case #x: p1,p2,⋯,pmp1,p2,⋯,pm”, where x is the case number (starting from 1) and p1,p2,⋯,pmp1,p2,⋯,pm is the answer.
Sample Input
2
5 2
3 3 1 5 4
2 2
4 4
5 2
2 5 2 1 2
2 3
2 4
Sample Output
Case #1: 3 3
Case #2: 3 1
思路:
主席树有两个操作,一个查找区间的 不同数的个数,另一个是查找 第k 大的值。
这个题两个都考到了,
查找区间不同数的个数的时候,需要按照位置插入。好巧的是,这个题要查找 位置的 第 k 大值。
即这个题是按照 位置 插入,并不是数值的大小。 而且是倒着插入位置。
题目要求保存每个数第一次出现的位置,所以我们就可以从后往前插入。
在当前版本的线段树中,如果一个数已经出现,那就在之前出现的位置 -1.。当前位置 + 1.
#include<bits/stdc++.h>
using namespace std;
const int N = 2e5+10;
struct segtree
{
int l,r,sum;
}f[N*36];
int rt[N],cnt,n,m,pre[N],a[N];
void update(int &now, int a, int b, int k, int val){
f[++cnt] = f[now];
f[now = cnt].sum += val;
if (a + 1 == b) return;
int mid = (a + b) / 2;
if (k < mid) update(f[now].l,a,mid,k,val); else
update(f[now].r,mid,b,k,val);
}
int find(int now, int l, int r, int a, int b){
int ans = 0;
if (a <= l && b >= r-1){
return f[now].sum;
}
int mid = (l + r) / 2;
if (a < mid) ans += find(f[now].l,l,mid,a,b);
if (b >= mid) ans += find(f[now].r,mid,r,a,b);
return ans;
}
int Query(int now, int l, int r, int k){
if (l + 1 == r) return l;
int mid = (l + r) / 2;
if (f[f[now].l].sum >= k) return Query(f[now].l,l,mid,k);
return Query(f[now].r,mid,r,k-f[f[now].l].sum);
}
int main(){
int T,tt=1; scanf("%d",&T);
while(T--){
printf("Case #%d:",tt++);
memset(pre,-1,sizeof(pre));
memset(f,0,sizeof(f));
cnt = 0;
scanf("%d%d",&n,&m);
for (int i = 1; i <= n; ++i)
scanf("%d",&a[i]);
rt[n+1] = 0;
for (int i = n; i >= 1; --i){
rt[i] = rt[i+1];
update(rt[i],1,n+1,i,1);
if (pre[a[i]] != -1){
update(rt[i],1,n+1,pre[a[i]],-1);
}
pre[a[i]] = i;
}
int lr;
lr = 0;
for (int i = 0; i < m; ++i){
int x,y,z,xx,yy;
scanf("%d%d",&x,&y);
xx = min(((x+lr) % n) +1,((y+lr)%n)+1);
yy = max(((x+lr) % n) +1,((y+lr)%n)+1);
z = (find(rt[xx],1,n+1,xx,yy) + 1) >> 1;
lr = Query(rt[xx],1,1+n,z);
printf(" %d",lr);
}
printf("\n");
}
return 0;
}