题意:给你n个数字,给你查询l,r,让你得出1-l 加上r-n里不同的数有几个。
思路:直接把1-n的数字再重新连接到1-n后面,这样你只要求解[r,l+n]的区间不同个数,但是如果l比r大的话,就是求整个区间。预处理完后,再用线段树操作。
代码:
#include<iostream>
#include<algorithm>
#include<cstring>
#include<stdio.h>
#include<map>
using namespace std;
const int N=2e5+10;
int n,m;
int cnt[N<<2];
struct qu
{
int l,r,i;
}q[N];
bool cmp(qu a,qu b)
{
if(a.r!=b.r)return a.r<b.r;
else return a.l<b.l;
}
void update(int x,int k,int num,int l,int r)
{
if(l==r&&l)
{
cnt[x]=num;
return;
}
int mid=(l+r)/2;
if(k<=mid)update(x*2,k,num,l,mid);
else update(x*2+1,k,num,mid+1,r);
cnt[x]=cnt[x*2]+cnt[x*2+1];
}
int query(int x,int l,int r,int L,int R)
{
if(l<=L&&r>=R)
{
return cnt[x];
}
int mid=(L+R)/2;
int s=0;
if(l<=mid)s+=query(x*2,l,r,L,mid);
if(r>mid)s+=query(x*2+1,l,r,mid+1,R);
return s;
}
int a[N],b[N];
int ans[N];
int main()
{
int n,m;
while(~scanf("%d%d",&n,&m))
{
memset(cnt,0,sizeof(cnt));
memset(ans,0,sizeof(ans));
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
b[i]=a[i];
}
for(int i=n+1;i<=2*n;i++)
{
b[i]=a[i-n];
}
int z=2*n;
for(int i=1;i<=m;i++)
{
scanf("%d%d",&q[i].l,&q[i].r);
if(q[i].l>q[i].r)
{
q[i].l=1;
q[i].r=n;
q[i].i=i;
continue;
}
int k=q[i].l+n;
q[i].l=q[i].r;
q[i].r=k;
q[i].i=i;
}
sort(q+1,q+m+1,cmp);
map<int,int>mp;
mp.clear();
for(int i=1,j=1;i<=2*n;i++)
{
if(mp[b[i]])
{
update(1,mp[b[i]],0,1,z);
update(1,i,1,1,z);
mp[b[i]]=i;
}
else
{
update(1,i,1,1,z);
mp[b[i]]=i;
}
while(j<=m&&q[j].r==i)
{
ans[q[j].i]=query(1,q[j].l,q[j].r,1,z);
j++;
}
}
for(int i=1;i<=m;i++)
{
printf("%d\n",ans[i]);
}
}
return 0;
}