若想要深入学习主席树,传送门。
Description:
给定数列 ({a_n}) ,求闭区间 ([l,r]) 的互异的个数。
Method:
扫描序列建立可持续化线段树,若此元素是第一次出现,就将对应的线段树中的位置加1;反之,就将上一个出现的元素对应的线段树中的位置减1,将此元素对应的线段树中的位置加1。
对于查询的 ([l,r]) ,在第 (r) 个版本的线段树上查询位置 (l) ,对经过的区间中的和累加一下即可。
Code:
#include<bits/stdc++.h>
#define int long long
#define Maxn 30010
using namespace std;
inline void read(int &x)
{
int f=1;x=0;char s=getchar();
while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}
x*=f;
}
int n,q;
int a[Maxn];
int tot=0;
struct Segtree
{
int ls,rs,sum;
}tree[Maxn<<5];
int rt[Maxn];
inline void Init(){tot=0;}
inline void pushup(int rt)
{
tree[rt].sum=tree[tree[rt].ls].sum+tree[tree[rt].rs].sum;
}
inline int build(int l,int r)
{
int rt=++tot;
if(l==r)
{
tree[rt].sum=0;
return rt;
}
int mid=(l+r)/2;
tree[rt].ls=build(l,mid);
tree[rt].rs=build(mid+1,r);
pushup(rt);
return rt;
}
int update(int k,int l,int r,int root,int val)
{
int rt=++tot;
tree[rt]=tree[root];
if(l==k&&r==k)
{
tree[rt].sum+=val;
return rt;
}
int mid=(l+r)/2;
if(k<=mid) tree[rt].ls=update(k,l,mid,tree[rt].ls,val);
else tree[rt].rs=update(k,mid+1,r,tree[rt].rs,val);
pushup(rt);
return rt;
}
int query(int l,int r,int rt,int pos)
{
if(l==r) return tree[rt].sum;
int mid=(l+r)/2;
if(pos<=mid) return tree[tree[rt].rs].sum+query(l,mid,tree[rt].ls,pos);
else return query(mid+1,r,tree[rt].rs,pos);
}
map<int,int>mp;
signed main()
{
Init();
read(n);
for(int i=1;i<=n;i++)
{
read(a[i]);
}
rt[0]=build(1,n);
for(int i=1;i<=n;i++)
{
if(mp.find(a[i])==mp.end())
{
mp[a[i]]=i;
rt[i]=update(i,1,n,rt[i-1],1);
}else
{
int tmprt=update(mp[a[i]],1,n,rt[i-1],-1);
rt[i]=update(i,1,n,tmprt,1);
}
mp[a[i]]=i;
}
read(q);
while(q--)
{
int l,r;
read(l),read(r);
int ans=query(1,n,rt[r],l);
printf("%lld
",ans);
}
return 0;
}