扫描序列建立可持续化线段树。
对每个pos建立主席树。pos对应单点修改的位置,找到+=val;
如果未出现,就赋值当前位置去找。不然先在root[i-1]基础上减去上次这个数字的贡献,再加这次这个数字的贡献。
同时维护每一个元素上一次出现的位置。
query的时候:每一个主席树维护的是1~i(当前位置)的不同数字有多少个。
找到时候在root【r】中找,树中区间编号>=l的,就是1-r中,l~r的贡献所求。
理解可以结合代码模拟一下样例。
#include<iostream>
#include<vector>
#include<queue>
#include<cstring>
#include<cmath>
#include<map>
#include<set>
#include<cstdio>
#include<algorithm>
#define debug(a) cout<<#a<<"="<<a<<endl;
using namespace std;
const int maxn=30010;
typedef long long LL;
struct Tree{
LL lson,rson,sum;
}tree[maxn<<5];
LL a[maxn],last[1000010],root[maxn];///数据还比较小不用离散化
LL tot=0;
void push_up(LL p){
tree[p].sum=tree[tree[p].lson].sum+tree[tree[p].rson].sum;
}
///权值线段树也是线段树,不过是对每个区间对应的位置+1,同样可以push_up
LL update(LL pre,LL l,LL r,LL pos,LL val){
LL rt=++tot;
tree[rt].sum=tree[pre].sum;///新建点先附着上一个信息
tree[rt].lson=tree[pre].lson;
tree[rt].rson=tree[pre].rson;
if(l==r) {///叶子节点进行单点修改
tree[rt].sum+=val;
return rt;
}
LL mid=(l+r)>>1;
if(pos<=mid) tree[rt].lson=update(tree[pre].lson,l,mid,pos,val);
else tree[rt].rson=update(tree[pre].rson,mid+1,r,pos,val);
push_up(rt);
return rt;
}
///查询>=l区间的区间总和
LL query(LL l,LL r,LL rt,LL pos){
if(l==r) return tree[rt].sum;
LL mid=(l+r)>>1;
if(pos<=mid) return tree[tree[rt].rson].sum+query(l,mid,tree[rt].lson,pos);
else return query(mid+1,r,tree[rt].rson,pos);
}
int main(void)
{
cin.tie(0);std::ios::sync_with_stdio(false);
LL n;cin>>n;
for(LL i=1;i<=n;i++){
cin>>a[i];
}
for(LL i=1;i<=n;i++){
if(!last[a[i]]){
last[a[i]]=i;
root[i]=update(root[i-1],1,n,i,1);
}
else{
LL tmp=update(root[i-1],1,n,last[a[i]],-1);
root[i]=update(tmp,1,n,i,1);
last[a[i]]=i;
}
}
LL Q;cin>>Q;
while(Q--){
LL l,r;cin>>l>>r;
cout<<query(1,n,root[r],l)<<endl;
}
return 0;
}