Description
给出一个长度为 n n 的序列和 q q 查询,每次查询表示 a1,a2,...,ai,aj,aj+1,...,an a 1 , a 2 , . . . , a i , a j , a j + 1 , . . . , a n 中不同数字的个数
Input
多组用例,每组用例首先输入两个整数 n,q n , q ,之后输入 n n 个整数,最后 q q 行每行两个整数
(1≤n,q≤105,1≤ai≤n,1≤li,rl≤n) ( 1 ≤ n , q ≤ 10 5 , 1 ≤ a i ≤ n , 1 ≤ l i , r l ≤ n )
Output
对于每组查询输出查询结果
Sample Input
3 2
1 2 1
1 2
1 3
4 1
1 2 3 4
1 3
Sample Output
2
1
3
Solution
首先复制 a a 序列为,同样记该序列为 a a ,那么对原序列的查询即为求新序列 ar,...,al+n a r , . . . , a l + n 中出现的数字种类数,进而把对两个区间的查询变成一段连续区间。查询序列区间 [l,r] [ l , r ] 中数字种类数,即看每种数字在该区间是否出现过,进而等价于判断区间 [1,r] [ 1 , r ] 中该数字个数减去区间 [1,l−1] [ 1 , l − 1 ] 中该数字个数是否为 0 0
对每种数字得到其第一次出现的位置,在树状数组中该位置加一,之后对于每种数字,从第一次出现的位置一个个往后跳到该种数字的位置,一直到跳入区间为止,那么此时由树状数组可以求出 [1,r] [ 1 , r ] 中该种数字出现的次数以及 [1,l−1] [ 1 , l − 1 ] 中该种数字出现的次数,两者相减若为 1 1 说明区间中出现过该数字。对于多组查询,把查询离线按左端点排序,每次从不超过左端点的新位置往后跳,每次把跳到的位置在树状数组中加一表示该种数字数量加一,跳入区间 [l,r] [ l , r ] 则停即可
Code
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#include<queue>
#include<map>
#include<set>
#include<ctime>
using namespace std;
typedef long long ll;
#define maxn 200005
int n,q,a[maxn],Next[maxn],ans[maxn],pos[maxn];
struct BIT
{
#define lowbit(x) (x&(-x))
int b[maxn];
void init()
{
memset(b,0,sizeof(b));
}
void add(int x)
{
while(x<=n)
{
b[x]++;
x+=lowbit(x);
}
}
int sum(int x)
{
int ans=0;
while(x)
{
ans+=b[x];
x-=lowbit(x);
}
return ans;
}
}bit;
struct query
{
int l,r,id;
bool operator<(const query&b)const
{
return l<b.l;
}
}e[maxn];
int main()
{
while(~scanf("%d%d",&n,&q))
{
for(int i=1;i<=n;i++)scanf("%d",&a[i]),a[i+n]=a[i];
for(int i=1;i<=q;i++)
{
scanf("%d%d",&e[i].r,&e[i].l);
e[i].r+=n;
e[i].id=i;
}
sort(e+1,e+q+1);
bit.init();
memset(pos,0,sizeof(pos));
n+=n;
for(int i=n;i>=1;i--)
{
Next[i]=pos[a[i]];
pos[a[i]]=i;
}
memset(pos,0,sizeof(pos));
for(int i=1;i<=n;i++)
{
if(!pos[a[i]])bit.add(i);
pos[a[i]]=i;
}
for(int i=1,j=1;i<=q;i++)
{
while(j<e[i].l)
{
if(Next[j])bit.add(Next[j]);
j++;
}
ans[e[i].id]=bit.sum(e[i].r)-bit.sum(e[i].l-1);
}
for(int i=1;i<=q;i++)printf("%d\n",ans[i]);
}
return 0;
}