写给自己,区间k个不同数的查询

前言

今天考场上忘了,只记得使用主席树.所以特意来补一篇.

静态

不带修改,进行查询.
如果只是单纯的对每一个位置建一颗主席树,在查询时进行减法的话,显然无法做到直接出结果。为此,我们可以在任意一棵主席树上规定[l,r]的值是这一段区间内有多少种不同的颜色。如果 l < p o s 1 < p o s 2 < r 并且 p o s 1 , p o s 2 的颜色相同,那我们规定pos1处没有颜色,pos2处有颜色.永远保证更靠近r的那一边有颜色.这样区间查询时,查以r为根的树,并且始终保证位置在l的右边。
对于每一个颜色记录一个前驱.这样就能够删除自己之前出现的点的贡献.

code

#include<bits/stdc++.h>
#define _ 30011
#define LL long long
using namespace std;
int rt[_],c[_*20][2],val[_*40],last[1000010];
inline char gc(){
    static char buf[1<<13],*p1=buf,*p2=buf;
    return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,1<<13,stdin),p1==p2)?EOF:*p1++;
}
inline int read(){
    register int data=0,w=1;
    char ch=0;
    while(ch<'0'||ch>'9'){ch=gc();if(ch == '-')w=-1;}
    while(ch<='9'&&ch>='0'){
        data=(data<<3)+(data<<1)+(ch^48);
        ch=gc();
    }
    return w*data;
}
int s[_],cnt;
int n,q,limit,reff[_];
inline void update(int T){
    val[T]=val[c[T][1]]+val[c[T][0]];
    return;
} 
inline void build(int pre,int &T,int l,int r,int pos,int w){
    T=++cnt;

    if(l==r){
        val[T]=val[pre]+w;
        return;
    }
    register int mid = (l+r)>>1;
    if(pos<=mid){
        c[T][1]=c[pre][1];
        build(c[pre][0],c[T][0],l,mid,pos,w);
    }
    else {
        c[T][0]=c[pre][0];
        build(c[pre][1],c[T][1],mid+1,r,pos,w);
    }
    update(T);
    return;
}
void fix(register int T,register int l,register int r,register int pos){
    if(l==r){
        val[T]-=1;
        return;
    }
    register int mid=(l+r)>>1;
    if(pos<=mid)fix(c[T][0],l,mid,pos);
    else fix(c[T][1],mid+1,r,pos);
    update(T);
    return;
}
inline int query(int T,register int l,register int r,register int pos){
    if(l==r)return val[T];
    register int mid = (l+r)>>1;
    if(mid>=pos)return val[c[T][1]]+query(c[T][0],l,mid,pos);
    else return query(c[T][1],mid+1,r,pos);
    /*if(pos<mid+1)return val[c[T][1]]+query(c[T][0],l,mid,pos);
    return query(c[T][1],mid+1,r,pos);*/
}
void yi(int &T,int l,int r){
    T=++cnt;
    if(l==r)return;
    register int mid = (l+r)>>1;
    yi(c[T][0],l,mid);
    yi(c[T][1],mid+1,r);
    return;
}
void dfs(int T,int l,int r){
    if(!T)return;
    register int mid  = (l+r)>>1;
    cout<<val[T]<<' '<<l<<' '<<r<<endl;
    dfs(c[T][0],l,mid);
    dfs(c[T][1],mid+1,r);
}
int main(){
    n=read();
    for(register int i=1;i<=n;++i)s[i]=read(),limit=max(limit,s[i]);
    yi(rt[0],1,n);
    for(register int i=1;i<=n;++i){
        if(last[s[i]]){
            //cout<<s[i]<<' '<<last[s[i]]<<endl;
            register int gg;
            build(rt[i-1],gg,1,n,i,1);
            build(gg,rt[i],1,n,last[s[i]],-1);
        }
        else {
            build(rt[i-1],rt[i],1,n,i,1);
        }
        last[s[i]]=i;
    }
    //dfs(rt[2],1,n);//return 0;
    q=read();
    register int L,R;
    for(register int i=1;i<=q;++i){
        L=read(),R=read();
        printf("%d\n",query(rt[R],1,n,L));
    }
}

动态

但动态怎么处理?最容易的的思路是修改树上的信息。当题目中出现把某处位置的颜色更改时,需要删除改点原来颜色的贡献,新增现在颜色的贡献.如果该位置是 p o s ,那么 p o s n 的位置都要修改。这个时候就要使用树套树。外层的树状数组支持快速修改.如果就在刚才静态的上面修改一下代码,最大的困难点在于询问。树状数组套用下的区间,维护的是一段区间的情况。
所以要彻底更换维护方式.
首先主席树不能用了.(树套树很少有主席树).采用树状数组套线段树.,每一个位置开一颗,每一棵线段树的意义是,其维护所有颜色的前驱的位置在其前面的信息.很显然,如果一段区间l,r想要询问的话,其一定会去l的线段树内找,因为存在l-r重复的话重复的那一个的前驱会在l-r中,l维护的是前驱小于l的.
现在考虑修改。这个时候就要用splay(建议 set)。分为删除与插入.

删除
  1. 这个点的前驱那里保留了他的信息,需要删掉。
  2. 他曾经作为别人的前驱保留过别人的前驱,删掉。
  3. 新加入的颜色在他的前驱那删掉他的后继的信息.
加入
  1. 这个点的后继要把他的信息加入到这个点的前驱.
  2. 新加入的颜色,在他的前驱那加入自己的信息.
  3. 新加入的颜色,作为他的后继的新前驱加入信息.
查询

树状数组上查询即可.

code

// luogu-judger-enable-o2
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<set>
#include<algorithm>
#define lowbit(k) (k&(-k))
using namespace std;
const int _ (10010);
const int __ (1e6+1e2);
int rt[_],c[_*150][2],val[_*150],cnt;//树的基本
set <int> s[__];//颜色基本;
set <int> ::iterator pre_it;//迭代器基本
set <int >::iterator it;
set <int >::iterator las_it;
int N,M,col[_];

void fix(int &T,int l,int r,const int loc,const int w){
    if(!T)T=++cnt;
    val[T]+=w;
    if(l==r)return;
    register int mid=(l+r)>>1;
    loc<=mid?fix(c[T][0],l,mid,loc,w):fix(c[T][1],mid+1,r,loc,w);
    return;
}

inline void pre_fix(register int k,register int w,register int loc){
    for(++k;k<=N;k+=lowbit(k))fix(rt[k],1,N,loc,w);
    return;
}

int query(int T,const int ql,const int qr,int l,int r){
    if(!T)return 0;
    if(l==r)return val[T];
    if(ql<=l&&qr>=r)return val[T];
    register int mid = (l+r)>>1,res=0;
    if(ql<=mid)res+=query(c[T][0],ql,qr,l,mid);
    if(qr>mid)res+=query(c[T][1],ql,qr,mid+1,r);
    return res;
}
inline int pre_query(register int l,register int r){
    register int p=l,res=0;
    for(;p;p-=lowbit(p))res+=query(rt[p],l,r,1,N);
    return res;
} 
set <int >::iterator operator - (set<int >::iterator a,int b){
    set <int >::iterator d=a;
    for(register int i=1;i<=b;++i)d--;
    return d;
}
int main(){
    std::ios::sync_with_stdio(false);
    cin>>N>>M;
    register int i,j,l,r,pre_col,now_col;
    register char ch;
    for(i=1;i<=N;++i){
        cin>>col[i];
        s[col[i]].empty()?pre_fix(0,1,i):pre_fix(*(s[col[i]].end()-1),1,i);
        s[col[i]].insert(i);  
    }

    for(i=1;i<=M;++i){
        cin>>ch;
        if(ch=='Q'){
            cin>>l>>r;
            printf("%d\n",pre_query(l,r));
        }
        else {
            cin>>l>>now_col;//注:把第l只笔换成r颜色
            if(col[l]==now_col)continue;
            pre_col=col[l];
            col[l]=now_col;
            it=s[pre_col].find(l);
            pre_it=it,--pre_it;
            las_it=it,++las_it;
            it==s[pre_col].begin()?pre_fix(0,-1,l):pre_fix(*pre_it,-1,l);
            //if(i==8)return 0;
            if(it!=(s[pre_col].end()-1)){
                pre_fix(*it,-1,*las_it);
                it==s[pre_col].begin()?pre_fix(0,1,*las_it):pre_fix(*pre_it,1,*las_it);
            }
            s[pre_col].erase(l);
            //以上是删除

            s[now_col].insert(l);
            it=s[now_col].find(l);
            pre_it=it,--pre_it;
            las_it=it,++las_it;
            if(it==s[now_col].begin()&&it==(s[now_col].end()-1)){
                pre_fix(0,1,*it);
                continue;
            }
            if(it==s[now_col].begin()){
                pre_fix(0,-1,*las_it);
                pre_fix(0,1,*it);
                pre_fix(*it,1,*las_it);
                continue;
            }
            if(it==(s[now_col].end()-1)){
                pre_fix(*pre_it,1,*it);

                continue;
            }
            pre_fix(*pre_it,-1,*las_it);
            pre_fix(*pre_it,1,*it);
            pre_fix(*it,1,*las_it);

        }
    }
}

猜你喜欢

转载自blog.csdn.net/JH_2002/article/details/81462255
今日推荐