题意:
给定 (N) 个数,以及 (Q) 个询问,每个询问给出 (L) 和 (R),现在问在这个区间最多可以选取多少个数,使得每个数出现次数不能大于 (K),强制在线。
分析:
当 (k=1) 时,本题就是求区间内不同的数的个数。因此,二者可以采用相同的方法来解决。
在求区间内不同数的个数的问题中,以位置建立主席树,记录每个数上一次出现的位置,当该数再次出现时,消除前一次出现的影响,记录这一次的影响。
本题中,用数组记录每个数最近 (k) 次出现的位置,当第 (k+1) 次出现时,把第 (1) 次出现的影响消除,记录下第 (k+1) 次的影响,以此类推。
代码:
#include <bits/stdc++.h>
#define pb push_back
using namespace std;
const int N=1e5+5;
struct node
{
int val,lson,rson;
}tree[N*40];
int root[N],tol;
vector<int>ap[N];
void pushup(int t)
{
tree[t].val=tree[tree[t].lson].val+tree[tree[t].rson].val;
}
int build(int l,int r)
{
int cnt=++tol;
if(l==r)
{
tree[cnt].val=0;
return cnt;
}
int mid=(l+r)>>1;
tree[cnt].lson=build(l,mid);
tree[cnt].rson=build(mid+1,r);
pushup(cnt);
return cnt;
}
int update(int l,int r,int pos,int w,int rt)
{
int cnt=++tol;
tree[cnt]=tree[rt];
if(l==r)
{
tree[cnt].val+=w;
return cnt;
}
int mid=(l+r)>>1;
if(pos<=mid)
tree[cnt].lson=update(l,mid,pos,w,tree[rt].lson);
else
tree[cnt].rson=update(mid+1,r,pos,w,tree[rt].rson);
pushup(cnt);
return cnt;
}
int query(int l,int r,int L,int R,int rt)
{
if(L<=l&&r<=R)
return tree[rt].val;
int mid=(l+r)>>1,res=0;
if(L<=mid)
res+=query(l,mid,L,R,tree[rt].lson);
if(R>mid)
res+=query(mid+1,r,L,R,tree[rt].rson);
return res;
}
int main()
{
int n,k,a,q,x,y,l,r,ans=0;
tol=0;
scanf("%d%d",&n,&k);
root[0]=build(1,n);
for(int i=1;i<=n;i++)
{
scanf("%d",&a);
ap[a].pb(i);
if(ap[a].size()>k)
{
root[i]=update(1,n,ap[a][0],-1,root[i-1]);
root[i]=update(1,n,i,1,root[i]);
ap[a].erase(ap[a].begin());
}
else
root[i]=update(1,n,i,1,root[i-1]);
}
scanf("%d",&q);
while(q--)
{
scanf("%d%d",&x,&y);
l=(x+ans)%n+1;
r=(y+ans)%n+1;
if(l>r) swap(l,r);
ans=query(1,n,l,r,root[r]);
printf("%d
",ans);
}
return 0;
}