离线做法:
枚举右端点R,右端点固定时,记录最右边第一次出现的数字为1,比如1 2 1 2 3,0 0 1 1 1 .给定L,那么L-R区间数字种类的结果是:sum[R]-sum[L-1].
把询问按R从小到大排序,我们R+1时,维护的数组只改变R+1为1,和R+1的数之前出现的位置变为0即可。
区间种类 查询 主席树在线做法:把上述过程在线,即每个R建一棵线段树就行。
//KX
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
//unordered_map<int,int>mp;
const int M= 1e5+7;
int a[M];
int root[M*26],st[M*26];
int ls[M*26],rs[M*26];
int cnt;
void up(int pre,int &o,int l,int r,int x,int d)
{
ls[++cnt]=ls[pre];
rs[cnt]=rs[pre];
st[cnt]=st[pre]+d;
o=cnt;
if(l>=r)return ;
int m=(l+r)/2;
if(x<=m)up(ls[pre],ls[o],l,m,x,d);
else up(rs[pre],rs[o],m+1,r,x,d);
}
int qu(int o,int l,int r,int x)
{
if(x<=l)
{
return st[o];
}
int m=(l+r)/2;
if(x<=m)return qu(ls[o],l,m,x)+qu(rs[o],m+1,r,x);
else return qu(rs[o],m+1,r,x);
}
map<int ,int >mp;
int main()
{
int n,q;
while(~scanf("%d",&n))
{
int tmp=0;
cnt=0;mp.clear();
st[0]=ls[0]=rs[0]=root[0]=0;
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
if(mp[a[i]]==0)
{
up(root[i-1],root[i],1,n,i,1);
}
else
{
up(root[i-1],tmp,1,n,mp[a[i]],-1);
up(tmp,root[i],1,n,i,1);
}
mp[a[i]]=i;
}
scanf("%d",&q);
for(int i=1;i<=q;i++)
{
int l,r;
scanf("%d%d",&l,&r);
printf("%d\n",qu(root[r],1,n,l));
}
}
return 0;
}