题意:给出一段序列,然后有q个询问,要求求出下标l-r的出现数的种数。
在线方法就可以用主席树,这里T[i]是根据i-n的数建立的线段数,每次更新时,如果当前的数在上一个状态没有出现过,可以直接插入,出现一条新链,如果出现过,应该先把这个数擦出,然后在这个擦除的状态上更新,这样查询的时候就只要查询T[l]这个线段数上1-r的和了,[1,l-1]的和应该是0,因为对于T[l],前面都没有插入到这颗线段树。。
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<map>
using namespace std;
const int MAXN=30010;
int tot,a[MAXN],n;
int T[MAXN];
int lson[MAXN*100],rson[MAXN*100],c[MAXN*100];
int build(int l,int r)
{
int root=tot++;
c[root]=0;
int mid=(l+r)>>1;
if(l!=r)
{
lson[root]=build(l,mid);
rson[root]=build(mid+1,r);
}
return root;
}
int insert(int root,int pos,int val)
{
int newroot=tot++;
int tmp=newroot;
c[newroot]=c[root]+val;
int l=1,r=n;
while(l<r)
{
int mid=(l+r)>>1;
if(pos<=mid)
{
lson[newroot]=tot++;
rson[newroot]=rson[root];
newroot=lson[newroot];
root=lson[root];
r=mid;
}
else
{
rson[newroot]=tot++;
lson[newroot]=lson[root];
newroot=rson[newroot];
root=rson[root];
l=mid+1;
}
c[newroot]=c[root]+val;
}
return tmp;
}
int query(int root,int pos)
{
int ans=0;
int l=1,r=n;
while(l<r)
{
int mid=(l+r)>>1;
if(pos<=mid)
{
root=lson[root];
r=mid;
}
else
{
ans+=c[lson[root]];
root=rson[root];
l=mid+1;
}
}
return ans+c[root];
}
map<int,int> mp;
int main()
{
int i;
while(scanf("%d",&n)==1)
{
for(i=1;i<=n;i++)
scanf("%d",&a[i]);
tot=0;
T[n+1]=build(1,n);
mp.clear();
for(i=n;i>=1;i--)
{
if(mp.find(a[i])==mp.end())
T[i]=insert(T[i+1],i,1);
else
{
int tmp=insert(T[i+1],mp[a[i]],-1);
T[i]=insert(tmp,i,1);
}
mp[a[i]]=i;
}
int q;
scanf("%d",&q);
while(q--)
{
int l,r;
scanf("%d%d",&l,&r);
printf("%d\n",query(T[l],r));
}
}
return 0;
}