题解:
主要是想到要将题目转化为求大于r的数量;
首先开一个数组b,b[i]记录的是i往后数k个a[i]的位置,如果没有k个则b[i]=n+1;
首先看一个式子
ans=query(1,n+1,rt[r],r+1,2e5)-query(1,n+1,rt[l-1],r+1,2e5);
每个询问代表的是我们要从1,n+1找,区间为r+1到2e5的数量为多少。
因为每个b[i]记录的是i往后数k个a[i]的位置,那么l到r之间落到r+1以后的都将会被计算到,因为我们用的是b[i]去插入主席树,那么在l到r之间的数量大于k的数只会计算k次,因为只有最多k个数落在r+1到2e5.
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <cmath>
#include <map>
#define FOR(i,a,b) for(int i=a;i<=b;i++)
#define ROF(i,a,b) for(int i=a;i>=b;i--)
#define mem(i,a) memset(i,a,sizeof(i))
#define mp make_pair
using namespace std;
typedef long long ll;
const int maxn = 1e5+5;
int n,k,q,x,y,cnt=0;
int a[maxn],b[maxn];
vector<int> pos[maxn];
int rt[maxn*20],ls[maxn*20],rs[maxn*20],sum[maxn*20];
void update(int l,int r,int &y,int x,int pos){
y=++cnt;
ls[y] = ls[x];
rs[y] = rs[x];
sum[y] = sum[x]+1;
if(l==r) return;
int mid=(l+r)/2;
if(pos<=mid) update(l,mid,ls[y],ls[x],pos);
else update(mid+1,r,rs[y],rs[x],pos);
}
int query(int l,int r,int x,int L,int R){
if(l>=L&&r<=R) return sum[x];
int mid=(l+r)/2;
int ans=0;
if(mid>=L) ans+=query(l,mid,ls[x],L,R);
if(mid<R) ans+=query(mid+1,r,rs[x],L,R);
return ans;
}
void cal(){
ROF(i,n,1){
int sz= pos[a[i]].size();
if(sz<k) b[i]=n+1;
else b[i]=pos[a[i]][sz-k];
pos[a[i]].push_back(i);
}
}
int main()
{
scanf("%d%d",&n,&k);
FOR(i,1,n) scanf("%d",&a[i]);
cal();
FOR(i,1,n) update(1,n+1,rt[i],rt[i-1],b[i]);
scanf("%d",&q);
int last=0,l,r;
FOR(i,1,q){
scanf("%d%d",&l,&r);
l=((l+last)%n)+1;
r=((r+last)%n)+1;
if(l>r) swap(l,r);
int ans = query(1,n+1,rt[r],r+1,2e5)-query(1,n+1,rt[l-1],r+1,2e5);
printf("%d\n",ans);
last=ans;
}
return 0;
}