SP3267 DQUERY - D-query (求区间内不同数的个数-在线主席做法)

扫描序列建立可持续化线段树。

对每个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;
}

猜你喜欢

转载自blog.csdn.net/zstuyyyyccccbbbb/article/details/110358565