若想要深刻學習主席樹,傳送門。html
Description:c++
給定數列 \(\{a_n\}\) ,求閉區間 \([l,r]\) 的互異的個數。學習
Method:ui
掃描序列創建可持續化線段樹,若此元素是第一次出現,就將對應的線段樹中的位置加1;反之,就將上一個出現的元素對應的線段樹中的位置減1,將此元素對應的線段樹中的位置加1。spa
對於查詢的 \([l,r]\) ,在第 \(r\) 個版本的線段樹上查詢位置 \(l\) ,對通過的區間中的和累加一下便可。code
Code:htm
#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\n",ans); } return 0; }