题目链接:
题目大意:
给你一个长为n(n<=1e6)的数组,有m(m<=1e6)次询问,每次询问包含两个整数L,R,求区间[L,R]内共有几种不同的数字
题目解析:
对于若干个询问的区间[l,r],如果他们的r都相等的话,那么项链中出现的同一个数字,一定是只关心出现在最右边的那一个的,例如:
项链是:1 3 4 5 1
那么,对于r=5的所有的询问来说,第一个位置上的1完全没有意义,因为r已经在第五个1的右边,对于任何查询的[L,5]区间来说,如果第一个1被算了,那么他完全可以用第五个1来替代。
因此,我们可以对所有查询的区间按照r来排序,然后再来维护一个树状数组,这个树状数组是用来干什么的呢?看下面的例子:
1 2 1 3
对于第一个1,insert(1,1);表示第一个位置出现了一个不一样的数字,此时树状数组所表示的每个位置上的数字(不是它本身的值而是它对应的每个位置上的数字)是:1 0 0 0
对于第二个2,insert(2,1);此时树状数组表示的每个数字是1 1 0 0
对于第三个1,因为之前出现过1了,因此首先把那个1所在的位置删掉insert(1,-1),然后在把它加进来insert(3,1)。此时每个数字是0 1 1 0
如果此时有一个询问[2,3],那么直接求sum(3)-sum(2-1)=2就是答案。
AC代码:
#include<iostream>
#include<cstring>
#include<cstdio>
#include<stack>
#include<algorithm>
using namespace std;
#define ll long long
const int MAXN=1000000+10;
int n,m,c[MAXN],a[MAXN];
int flag[MAXN];
struct query
{
int l,r,num;
int ans;
}q[MAXN];
bool mycmp(query x,query y)
{
return x.r<y.r;
}
bool mycmp2(query x,query y)
{
return x.num<y.num;
}
inline int lowbit(int x) {return x&-x;}
int sum(int x)
{
int ret=0;
while(x>0)
{
ret+=c[x];
x-=lowbit(x);
}
return ret;
}
void add(int x,int d)
{
while(x<=n)
{
c[x]+=d;
x+=lowbit(x);
}
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
scanf("%d",&m);
for(int i=1;i<=m;i++)
{
scanf("%d%d",&q[i].l,&q[i].r);
q[i].num=i;
}
sort(q+1,q+m+1,mycmp);
for(int i=1;i<=m;i++)
{
for(int j=q[i-1].r+1;j<=q[i].r;j++)
{
if(flag[a[j]]) add(flag[a[j]],-1);
add(j,1);
flag[a[j]]=j;
}
//cout<<sum(q[i].r)<<endl;
q[i].ans=sum(q[i].r)-sum(q[i].l-1);
}
sort(q+1,q+m+1,mycmp2);
for(int i=1;i<=m;i++) printf("%d\n",q[i].ans);
}