zoukankan      html  css  js  c++  java
  • 主席树学习笔记

    主席树是一种极其强大的数据结构。

    记录了一颗线段树经过若干次修改的历史版本。

    可以用来水各种题

    原理

    首先,主席树是线段树的历史版本。

    即可持久化线段树。

    就是每次操作后的线段树的节点信息。

    这些节点信息若每次操作后都保留下来。

    对空间和时间的消耗都是巨大的。

    因此,我们需要可持久化线段树,主席树来储存。

    我们知道,线段树的左右儿子的编号是该节点编号的两倍和两倍加一。

    结构如下图所示:

    image

    对于一个已知的叶子节点,与他有关的节点只有(logn)个,即树高。

    因此,每次修改后,只有(logn)个节点发生了改变。

    那我们为什么要把所有的节点都复制一遍呢?

    我们可以共有不会改变的节点,以减少空间以及时间开销。

    那么,我们该如何共用节点同时又改变信息呢。

    我们只用新建一些会发生改变的信息的节点,并把记录该次操作后的根节点。

    image

    如图所示,蓝色节点为原有节点,而红色节点为新建节点,结合红色节点和蓝色节点我们可以求出完整为线段树。

    由图可见,主席树的左右儿子不再是原本线段树的左右儿子了。

    所以,我们在新建一个节点的同时,要继承上一次操作的节点的所有信息,然后在修改当前的信息。

    同时,对于一般的线段树,我们节点维护的信息一般都是序列的值(非权值)。

    而线段树要维护历史版本的信息,因此,我们不能利用普通的线段树维护信息的方式去维护。

    我们去维护的是一颗权值线段树由于主席树是权值线段树,我们可以直接在树上二分,减掉一个(log)的时间复杂度。),即线段树上维护的是权值的个数。

    这样,线段树就可以加减了。。。

    满足了可加减后,我们可以干很多事情。

    维护区间([L,R])可以转化为两个前缀和相减。

    大部分的题都可以解决了。

    题目

    HDU2665

    说到序列上的操作不得不提起经典的区间第(k)值。

    题目中给出了一些区间,要求求出这些区间的第(k)值。

    先来回忆一下,一个序列的第(k)值是怎么求的。

    我们可以二分我们要求的第(k)值,我们只要(check)即可。

    我们该如何去(check)呢?既然他是第(k)值,那小于等于他的数就有(k),按照这个二分即可。

    同时主席树是权值线段树,我们可以直接在主席树上二分时间复杂度(O(logn)),完美解决。

    查询区间第(k)值函数如下:

    int query(int pre,int nxt,int L,int R,int k) {
        if(L==R)return L;
        int mid=(L+R)>>1;
        int Sum=tot[Ls[nxt]]-tot[Ls[pre]];//左儿子区间节点数量
        if(Sum<k)return query(Rs[pre],Rs[nxt],mid+1,R,k-Sum);//递归到右儿子
        else return query(Ls[pre],Ls[nxt],L,mid,k);//递归到左儿子
    }
    

    代码如下

    #include <cstdio>
    #include <cstring>
    #include <vector>
    #include <iostream>
    #include <algorithm>
     
    using namespace std;
     
    #define LL long long
    #define reg register
    #define clr(a,b) memset(a,b,sizeof a)
    #define Mod(x) (x>=mod)&&(x-=mod)
    #define debug(x) cerr<<#x<<"="<<x<<endl;
    #define debug2(x,y) cerr<<#x<<"="<<x<<" "<<#y<<"="<<y<<endl;
    #define debug3(x,y,z) cerr<<#x<<"="<<x<<" "<<#y<<"="<<y<<" "<<#z<<"="<<z<<endl;
    #define rep(a,b,c) for(reg int a=(b),a##_end_=(c); a<=a##_end_; ++a)
    #define ret(a,b,c) for(reg int a=(b),a##_end_=(c); a<a##_end_; ++a)
    #define drep(a,b,c) for(reg int a=(b),a##_end_=(c); a>=a##_end_; --a)
    #define erep(i,G,x) for(int i=(G).Head[x]; i; i=(G).Nxt[i])
     
    inline int Read(void) {
        int res=0,f=1;
        char c;
        while(c=getchar(),c<48||c>57)if(c=='-')f=0;
        do res=(res<<3)+(res<<1)+(c^48);
        while(c=getchar(),c>=48&&c<=57);
        return f?res:-res;
    }
     
    template<class T>inline bool Min(T &a, T const&b) {
        return a>b?a=b,1:0;
    }
    template<class T>inline bool Max(T &a, T const&b) {
        return a<b?a=b,1:0;
    }
    const int N=1e5+5,M=2e5+5,mod=1e9+7;
     
    bool MOP1;
     
    struct Function_SEG {
        int cnt,Ls[N*20],Rs[N*20],tot[N*20];
        void Insert(int &Now,int pre,int L,int R,int x) {
            Now=++cnt;
            Ls[Now]=Ls[pre];
            Rs[Now]=Rs[pre];
            tot[Now]=tot[pre]+1;
            if(L==R)return;
            int mid=(L+R)>>1;
            if(x<=mid)Insert(Ls[Now],Ls[pre],L,mid,x);
            else Insert(Rs[Now],Rs[pre],mid+1,R,x);
            tot[Now]=tot[Ls[Now]]+tot[Rs[Now]];
        }
        int query(int pre,int nxt,int L,int R,int k) {
            if(L==R)return L;
            int mid=(L+R)>>1;
            int Sum=tot[Ls[nxt]]-tot[Ls[pre]];
            if(Sum<k)return query(Rs[pre],Rs[nxt],mid+1,R,k-Sum);
            else return query(Ls[pre],Ls[nxt],L,mid,k);
        }
    } tr;
     
    int A[N],B[N],root[N];
     
    bool MOP2;
     
    void _main(void) {
        int n=Read(),m=Read();
        rep(i,1,n)A[i]=B[i]=Read();
        sort(B+1,B+n+1);
        int len=unique(B+1,B+n+1)-B-1;
        rep(i,1,n) {
            A[i]=lower_bound(B+1,B+len+1,A[i])-B;
            tr.Insert(root[i],root[i-1],1,len,A[i]);
        }
        while(m--) {
            int L=Read(),R=Read(),k=R-L+2-Read();
            int Now=tr.query(root[L-1],root[R],1,len,k);
            printf("%d
    ",B[Now]);
        }
    }
     
    signed main() {
        _main();
        return 0;
    }
    

    HDU3333

    我们需要求出区间不重复元素和。

    我们来回忆一下离线的做法,利用树状数组维护所有的排序好的区间。

    所以对于一个右端点,我们要保证从1到该端点的元素和没有重复的元素,于是我们仿照树状数组的做法对于一个相同的元素在之前减掉,同时建一颗主席树即可。

    这样我们就可以在线的解决掉这个问题了。

    #include <bits/stdc++.h>
    
    using namespace std;
    
    #define LL long long
    #define reg register
    #define debug(x) cerr<<#x<<" = "<<x<<endl;
    #define rep(a,b,c) for(reg int a=(b),a##_end_=(c); a<=a##_end_; ++a)
    #define ret(a,b,c) for(reg int a=(b),a##_end_=(c); a<a##_end_; ++a)
    #define drep(a,b,c) for(reg int a=(b),a##_end_=(c); a>=a##_end_; --a)
    
    inline int Read() {
        int res=0,f=1;
        char c;
        while(c=getchar(),c<48||c>57)if(c=='-')f=0;
        do res=(res<<3)+(res<<1)+(c^48);
        while(c=getchar(),c>=48&&c<=57);
        return f?res:-res;
    }
    
    template<class T>inline bool Min(T &a,T const&b) {
        return a>b?a=b,1:0;
    }
    template<class T>inline bool Max(T &a,T const&b) {
        return a<b?a=b,1:0;
    }
    
    const int N=30005;
    
    int n,m,cnt,root[N];
    
    struct HJT {
        int L,R,Lson,Rson;
        LL tot;
    } Tree[N*40];
    
    inline void up(int num) {
        Tree[num].tot=Tree[Tree[num].Lson].tot+Tree[Tree[num].Rson].tot;
    }
    
    int build(int L,int R) {
        int p=++cnt;
        Tree[p].L=L,Tree[p].R=R,Tree[p].tot=0;
        if(L==R)return p;
        int mid=(L+R)>>1;
        Tree[p].Lson=build(L,mid);
        Tree[p].Rson=build(mid+1,R);
        return p;
    }
    
    int update(int old,int pos,int val) {
        int p=++cnt;
        Tree[p]=Tree[old];
        if(Tree[p].L==Tree[p].R) {
            Tree[p].tot+=1ll*val;
            return p;
        }
        int mid=(Tree[p].L+Tree[p].R)>>1;
        if(pos<=mid)Tree[p].Lson=update(Tree[old].Lson,pos,val);
        else Tree[p].Rson=update(Tree[old].Rson,pos,val);
        up(p);
        return p;
    }
    
    LL Sum(int x,int L,int R) {
        if(Tree[x].L==L&&Tree[x].R==R)return Tree[x].tot;
        int mid=(Tree[x].L+Tree[x].R)>>1;
        LL Ans=0;
        if(R<=mid)Ans+=Sum(Tree[x].Lson,L,R);
        else if(L>mid)Ans+=Sum(Tree[x].Rson,L,R);
        else Ans+=Sum(Tree[x].Lson,L,mid)+Sum(Tree[x].Rson,mid+1,R);
        return Ans;
    }
    
    int A[N],B[N],pre[N];
    int main() {
        int T=Read();
        while(T--) {
            cnt=0;
            memset(pre,-1,sizeof pre);
            n=Read();
            root[0]=build(1,n);
            rep(i,1,n)A[i]=B[i]=Read();
            sort(B+1,B+n+1);
            int len=unique(B+1,B+n+1)-B-1;
            rep(i,1,n) {
                int x=A[i],id=lower_bound(B+1,B+len+1,A[i])-B;
                if(pre[id]==-1)root[i]=update(root[i-1],i,x);
                else {
                    int Now=update(root[i-1],pre[id],-x);
                    root[i]=update(Now,i,x);
                }
                pre[id]=i;
            }
            m=Read();
            rep(i,1,m) {
                int l=Read(),r=Read();
                printf("%lld
    ",Sum(root[r],l,r));
            }
        }
    }
    

    同时,主席树不一定是只能执行序列上的操作。

    能够执行很多树上以及图上的操作。

    大部分有前缀性质的都可以执行。

    像如果在树上前缀和,主席树的相减就要用到树上差分的思想相减。

    来看看这道题

    Count on a tree

    如此相似的区间第(k)值,那咋办呀?

    我们在询问区间的第(k)值时,要去维护一个序列的前缀,同样,树上的一条路径是唯一的一条路径。

    相似的,我们可以像维护序列的方法去维护一段树上的前缀和。

    #include <bits/stdc++.h>
    
    using namespace std;
    
    #define LL long long
    #define reg register
    #define debug(x) cerr<<#x<<" = "<<x<<endl;
    #define rep(a,b,c) for(reg int a=(b),a##_end_=(c); a<=a##_end_; ++a)
    #define ret(a,b,c) for(reg int a=(b),a##_end_=(c); a<a##_end_; ++a)
    #define drep(a,b,c) for(reg int a=(b),a##_end_=(c); a>=a##_end_; --a)
    
    inline int Read() {
    	int res=0,f=1;
    	char c;
    	while(c=getchar(),c<48||c>57)if(c=='-')f=0;
    	do res=(res<<3)+(res<<1)+(c^48);
    	while(c=getchar(),c>=48&&c<=57);
    	return f?res:-res;
    }
    
    template<class T>inline bool Min(T &a,T const&b) {
    	return a>b?a=b,1:0;
    }
    template<class T>inline bool Max(T &a,T const&b) {
    	return a<b?a=b,1:0;
    }
    
    const int N=1e5+5;
    
    int n,m,cnt,Case,root[N];
    
    struct HJT {
    	int Lson,Rson,tot;
    } Tree[N*40];
    
    int build(int L,int R) {
    	int p=++cnt;
    	Tree[p].tot=0;
    	if(L==R)return p;
    	int mid=(L+R)>>1;
    	Tree[p].Lson=build(L,mid);
    	Tree[p].Rson=build(mid+1,R);
    	return p;
    }
    
    int update(int old,int L,int R,int pos,int val) {
    	int p=++cnt;
    	Tree[p]=Tree[old];
    	if(L==R) {
    		Tree[p].tot+=val;
    		return p;
    	}
    	int mid=(L+R)>>1;
    	if(pos<=mid)Tree[p].Lson=update(Tree[old].Lson,L,mid,pos,val);
    	else Tree[p].Rson=update(Tree[old].Rson,mid+1,R,pos,val);
    	Tree[p].tot=Tree[Tree[p].Lson].tot+Tree[Tree[p].Rson].tot;
    	return p;
    }
    
    int A[N],B[N],Fa[N][20],dep[N],len;
    
    vector<int>edge[N];
    
    void dfs(int x,int pre) {
    	Fa[x][0]=pre,dep[x]=dep[pre]+1;
    	root[x]=update(root[pre],1,len,A[x],1);
    	ret(i,0,edge[x].size()) {
    		int y=edge[x][i];
    		if(y==pre)continue;
    		dfs(y,x);
    	}
    }
    
    inline int up(int x,int step) {
    	rep(i,0,19)if(step&(1<<i))x=Fa[x][i];
    	return x;
    }
    
    inline int lca(int a,int b) {
    	if(dep[a]>dep[b])swap(a,b);
    	b=up(b,dep[b]-dep[a]);
    	if(a==b)return a;
    	drep(i,19,0)if(Fa[a][i]!=Fa[b][i])a=Fa[a][i],b=Fa[b][i];
    	return Fa[a][0];
    }
    
    int ask(int x,int y,int z,int p,int l,int r,int kth) {
    	if(l==r)return l;
    	int mid=(l+r)>>1;
    	int ltot=Tree[Tree[x].Lson].tot+Tree[Tree[y].Lson].tot-Tree[Tree[z].Lson].tot-Tree[Tree[p].Lson].tot;
    	if(kth<=ltot)return ask(Tree[x].Lson,Tree[y].Lson,Tree[z].Lson,Tree[p].Lson,l,mid,kth);
    	else return ask(Tree[x].Rson,Tree[y].Rson,Tree[z].Rson,Tree[p].Rson,mid+1,r,kth-ltot);
    }
    
    int main() {
    	n=Read(),m=Read();
    	rep(i,1,n)A[i]=B[i]=Read();
    	sort(B+1,B+n+1);
    	len=unique(B+1,B+n+1)-B-1;
    	rep(i,1,n)A[i]=lower_bound(B+1,B+len+1,A[i])-B;
    	root[0]=build(1,len);
    	ret(i,1,n) {
    		int u=Read(),v=Read();
    		edge[u].push_back(v);
    		edge[v].push_back(u);
    	}
    	dfs(1,0);
    	rep(j,1,19)rep(i,1,n)Fa[i][j]=Fa[Fa[i][j-1]][j-1];
    	rep(i,1,m) {
    		int u=Read(),v=Read(),k=Read(),z=lca(u,v);
    		int Ans=ask(root[u],root[v],root[z],root[Fa[z][0]],1,len,k);
    		printf("%d
    ",B[Ans]);
    	}
    }
    

    区间修改的主席树。

    To the moon

    注意:主席树区间修改要标记永久化,不能延迟标记

    #include <bits/stdc++.h>
    
    using namespace std;
    
    #define LL long long
    #define reg register
    #define debug(x) cerr<<#x<<" = "<<x<<endl;
    #define rep(a,b,c) for(reg int a=(b),a##_end_=(c); a<=a##_end_; ++a)
    #define ret(a,b,c) for(reg int a=(b),a##_end_=(c); a<a##_end_; ++a)
    #define drep(a,b,c) for(reg int a=(b),a##_end_=(c); a>=a##_end_; --a)
    
    inline int Read() {
    	int res=0,f=1;
    	char c;
    	while(c=getchar(),c<48||c>57)if(c=='-')f=0;
    	do res=(res<<3)+(res<<1)+(c^48);
    	while(c=getchar(),c>=48&&c<=57);
    	return f?res:-res;
    }
    
    template<class T>inline bool Min(T &a,T const&b) {
    	return a>b?a=b,1:0;
    }
    template<class T>inline bool Max(T &a,T const&b) {
    	return a<b?a=b,1:0;
    }
    
    const int N=100005;
    
    int n,m,cnt,Case,root[N],A[N];
    
    struct HJT {
    	int Lson,Rson;
    	LL tot,laze;
    } Tree[N*25];
    
    int build(int L,int R) {
    	int p=++cnt;
    	Tree[p].tot=0;
    	if(L==R) {
    		Tree[p].tot+=1ll*A[L];
    		return p;
    	}
    	int mid=(L+R)>>1;
    	Tree[p].Lson=build(L,mid);
    	Tree[p].Rson=build(mid+1,R);
    	Tree[p].tot=Tree[Tree[p].Lson].tot+Tree[Tree[p].Rson].tot;
    	return p;
    }
    
    int update(int old,int l,int r,int L,int R,int val) {
    	int p=++cnt;
    	Tree[p]=Tree[old];Tree[p].tot+=1ll*val*(R-L+1);
    	if(l==L&&R==r) {
    		Tree[p].laze+=val;
    		return p;
    	}
    	int mid=(l+r)>>1;
    	if(R<=mid)Tree[p].Lson=update(Tree[old].Lson,l,mid,L,R,val);
    	else if(L>mid)Tree[p].Rson=update(Tree[old].Rson,mid+1,r,L,R,val);
    	else Tree[p].Lson=update(Tree[old].Lson,l,mid,L,mid,val),Tree[p].Rson=update(Tree[old].Rson,mid+1,r,mid+1,R,val);
    	return p;
    }
    
    LL Sum(int x,int l,int r,int L,int R) {
    	if(l==L&&r==R)return Tree[x].tot;
    	int mid=(l+r)>>1;
    	LL Ans=1ll*Tree[x].laze*(R-L+1);
    	if(R<=mid)return Ans+Sum(Tree[x].Lson,l,mid,L,R);
    	else if(L>mid)return Ans+Sum(Tree[x].Rson,mid+1,r,L,R);
    	else return Ans+Sum(Tree[x].Lson,l,mid,L,mid)+Sum(Tree[x].Rson,mid+1,r,mid+1,R);
    }
    
    int tim;
    
    char S[2];
    
    int main() {
    	n=Read(),m=Read();
    	rep(i,1,n)A[i]=Read();
    	root[0]=build(1,n);
    	rep(i,1,m) {
    		scanf("%s",S);
    		if(S[0]=='C') {
    			int L=Read(),R=Read(),d=Read();
    			root[++tim]=update(root[tim-1],1,n,L,R,d);
    		}
    		if(S[0]=='Q') {
    			int L=Read(),R=Read();
    			printf("%lld
    ",Sum(root[tim],1,n,L,R));
    		}
    		if(S[0]=='H') {
    			int L=Read(),R=Read(),T=Read();
    			printf("%lld
    ",Sum(root[T],1,n,L,R));
    		}
    		if(S[0]=='B')tim=Read();
    	}
    }
    

    ZOJ2112

    之前我们所遇到的第(k)值都是静态的。

    现在我们遇到的是动态区间第(k)值,我们需要换一种方法。

    我们对于维护的前缀和利用树状数组分割成(log_n)块子序列,利用分块的思想每次将以此操作分解成(log_n)次小操作。

    对于一次查询操作,我们将两个左右端点的(log_n)个有关节点加到两边,每次对于两边的前缀和进行统计操作。

    同样的对于每次修改操作,我们也将(log_n)个节点的主席树给改变掉。

    同时,由于这道题卡内存,我们不能正常的利用线段树去统计。

    于是,我们要开两个主席树去维护它,一颗是维护原序列,一颗是代表操作所需的主席树。

    #include <cstdio>
    #include <cstring>
    #include <vector>
    #include <iostream>
    #include <assert.h>
    #include <algorithm>
    
    using namespace std;
    
    #define LL long long
    #define reg register
    #define clr(a,b) memset(a,b,sizeof a)
    #define Mod(x) (x>=mod)&&(x-=mod)
    #define debug(x) cerr<<#x<<"="<<x<<endl;
    #define debug2(x,y) cerr<<#x<<"="<<x<<" "<<#y<<"="<<y<<endl;
    #define debug3(x,y,z) cerr<<#x<<"="<<x<<" "<<#y<<"="<<y<<" "<<#z<<"="<<z<<endl;
    #define rep(a,b,c) for(reg int a=(b),a##_end_=(c); a<=a##_end_; ++a)
    #define ret(a,b,c) for(reg int a=(b),a##_end_=(c); a<a##_end_; ++a)
    #define drep(a,b,c) for(reg int a=(b),a##_end_=(c); a>=a##_end_; --a)
    #define erep(i,G,x) for(int i=(G).Head[x]; i; i=(G).Nxt[i])
    
    inline int Read(void) {
    	int res=0,f=1;
    	char c;
    	while(c=getchar(),c<48||c>57)if(c=='-')f=0;
    	do res=(res<<3)+(res<<1)+(c^48);
    	while(c=getchar(),c>=48&&c<=57);
    	return f?res:-res;
    }
    
    template<class T>inline bool Min(T &a, T const&b) {
    	return a>b?a=b,1:0;
    }
    template<class T>inline bool Max(T &a, T const&b) {
    	return a<b?a=b,1:0;
    }
    const int N=5e4+5,M=1e4+5,mod=1e9+7;
    
    bool MOP1;
    
    int n,m,A[N],B[N<<1],root[N],Root[N],cnt1,cnt2,Lt[N],Rt[N],len;
    
    struct Function_SEG {
    	int cnt,Ls[N*50],Rs[N*50],tot[N*50];
    	inline void clear(void) {
    		cnt=0,clr(tot,0);
    	}
    	void Insert(int &Now,int od,int L,int R,int x,int v) {
    		Now=++cnt;
    		Ls[Now]=Ls[od],Rs[Now]=Rs[od],tot[Now]=tot[od]+v;
    		if(L==R)return;
    		int mid=(L+R)>>1;
    		if(x<=mid)Insert(Ls[Now],Ls[od],L,mid,x,v);
    		else Insert(Rs[Now],Rs[od],mid+1,R,x,v);
    	}
    	int query(int rt,int lt,int L,int R,int k) {
    		if(L==R)return L;
    		int mid=(L+R)>>1,Sum=tot[Ls[rt]]-tot[Ls[lt]];
    		rep(i,1,cnt1)Sum-=tot[Ls[Lt[i]]];
    		rep(i,1,cnt2)Sum+=tot[Ls[Rt[i]]];
    		if(Sum<k) {
    			rep(i,1,cnt1)Lt[i]=Rs[Lt[i]];
    			rep(i,1,cnt2)Rt[i]=Rs[Rt[i]];
    			return query(Rs[rt],Rs[lt],mid+1,R,k-Sum);
    		} else {
    			rep(i,1,cnt1)Lt[i]=Ls[Lt[i]];
    			rep(i,1,cnt2)Rt[i]=Ls[Rt[i]];
    			return query(Ls[rt],Ls[lt],L,mid,k);
    		}
    	}
    } tr;
    
    struct Que {
    	int op,L,R,K;
    } Q[N];
    
    char S[5];
    
    bool MOP2;
    
    void _main(void) {
    	int T=Read();
    	while(T--) {
    		clr(root,0),clr(Root,0);
    		tr.clear();
    		n=Read(),m=Read();
    		int tot=0;
    		rep(i,1,n)A[i]=B[++tot]=Read();
    		rep(i,1,m) {
    			scanf("%s",S);
    			if(S[0]=='Q')Q[i].op=1,Q[i].L=Read(),Q[i].R=Read(),Q[i].K=Read();
    			else Q[i].op=2,Q[i].L=Read(),B[++tot]=Q[i].R=Read();
    		}
    		sort(B+1,B+tot+1);
    		len=unique(B+1,B+tot+1)-B-1;
    		rep(i,1,n) {
    			A[i]=lower_bound(B+1,B+len+1,A[i])-B;
    			tr.Insert(root[i],root[i-1],1,len,A[i],1);
    		}
    		rep(i,1,m) {
    			if(Q[i].op==2) {
    				Q[i].R=lower_bound(B+1,B+len+1,Q[i].R)-B;
    				int pos=Q[i].L,x=A[Q[i].L],y=Q[i].R;
    				while(pos<=n) {
    					tr.Insert(Root[pos],Root[pos],1,len,x,-1);
    					tr.Insert(Root[pos],Root[pos],1,len,y,1);
    					pos+=pos&-pos;
    				}
    				A[Q[i].L]=Q[i].R;
    			} else {
    				cnt1=cnt2=0;
    				for(reg int j=Q[i].L-1; j; j-=j&-j)Lt[++cnt1]=Root[j];
    				for(reg int j=Q[i].R; j; j-=j&-j)Rt[++cnt2]=Root[j];
    				printf("%d
    ",B[tr.query(root[Q[i].R],root[Q[i].L-1],1,len,Q[i].K)]);
    			}
    		}
    	}
    }
    
    signed main() {
    	_main();
    	return 0;
    }
    

    [BZOJ3123][SDOI2013]森林

    还是区间第(k)值,由于题目中保证了任意时刻图都是一个森林。

    所以(x)(y)的路径是唯一的,我们又可以用上文的树上前缀和去维护了。。。

    由于有连边操作,就相当于合并两个主席树,直接启发式合并利用倍增维护(lca)即可。

    #include <bits/stdc++.h>
     
    using namespace std;
     
    #define LL long long
    #define reg register
    #define clr(a,b) memset(a,b,sizeof a)
    #define Mod(x) (x>=mod)&&(x-=mod)
    #define debug(x) cerr<<#x<<"="<<x<<endl;
    #define debug2(x,y) cerr<<#x<<"="<<x<<" "<<#y<<"="<<y<<endl;
    #define debug3(x,y,z) cerr<<#x<<"="<<x<<" "<<#y<<"="<<y<<" "<<#z<<"="<<z<<endl;
    #define rep(a,b,c) for(reg int a=(b),a##_end_=(c); a<=a##_end_; ++a)
    #define ret(a,b,c) for(reg int a=(b),a##_end_=(c); a<a##_end_; ++a)
    #define drep(a,b,c) for(reg int a=(b),a##_end_=(c); a>=a##_end_; --a)
    #define erep(i,G,x) for(int i=(G).Head[x]; i; i=(G).Nxt[i])
     
    inline int Read(void) {
        int res=0,f=1;
        char c;
        while(c=getchar(),c<48||c>57)if(c=='-')f=0;
        do res=(res<<3)+(res<<1)+(c^48);
        while(c=getchar(),c>=48&&c<=57);
        return f?res:-res;
    }
     
    template<class T>inline bool Min(T &a, T const&b) {
        return a>b?a=b,1:0;
    }
    template<class T>inline bool Max(T &a, T const&b) {
        return a<b?a=b,1:0;
    }
    const int N=8e4+5,M=8e4+5,mod=1e9+7;
     
    bool MOP1;
     
    struct Link_list {
        int Tot,Head[N],to[M<<1],Nxt[M<<1];
        inline void clear(void) {
            Tot=0,clr(Head,0);
        }
        inline void AddEdgepair(int a,int b) {
            to[++Tot]=b,Nxt[Tot]=Head[a],Head[a]=Tot;
            to[++Tot]=a,Nxt[Tot]=Head[b],Head[b]=Tot;
        }
    } G;
     
    int A[N],B[N],root[N];
     
    struct Function_SEG {
        int cnt,Ls[N*200],Rs[N*200],tot[N*200];
        void clear(void) {
            cnt=0,clr(Ls,0),clr(Rs,0),clr(tot,0);
        }
        void Insert(int &Now,int pre,int L,int R,int x) {
            Now=++cnt;
            Ls[Now]=Ls[pre],Rs[Now]=Rs[pre],tot[Now]=tot[pre]+1;
            if(L==R)return;
            int mid=(L+R)>>1;
            if(x<=mid)Insert(Ls[Now],Ls[pre],L,mid,x);
            else Insert(Rs[Now],Rs[pre],mid+1,R,x);
            tot[Now]=tot[Ls[Now]]+tot[Rs[Now]];
        }
        int query(int x,int y,int z,int p,int L,int R,int k) {
            if(L==R)return L;
            int mid=(L+R)>>1;
            int Sum=tot[Ls[x]]+tot[Ls[y]]-tot[Ls[z]]-tot[Ls[p]];
            if(k<=Sum)return query(Ls[x],Ls[y],Ls[z],Ls[p],L,mid,k);
            return query(Rs[x],Rs[y],Rs[z],Rs[p],mid+1,R,k-Sum);
        }
    } tr;
     
    int Sz[N],Id[N],Fa[N][18],dep[N],len;
    void dfs(int x,int pre,int tp) {
        Sz[Id[x]=tp]++,Fa[x][0]=pre,dep[x]=dep[pre]+1;
        rep(i,1,17)Fa[x][i]=Fa[Fa[x][i-1]][i-1];
        tr.Insert(root[x],root[pre],1,len,A[x]);
        erep(i,G,x) {
            int y=G.to[i];
            if(y==pre)continue;
            dfs(y,x,tp);
        }
    }
     
    inline int lca(int x,int y) {
        if(dep[x]>dep[y])swap(x,y);
        int step=dep[y]-dep[x];
        drep(i,17,0)if(step&1<<i)y=Fa[y][i];
        if(x==y)return x;
        drep(i,17,0)if(Fa[x][i]!=Fa[y][i])x=Fa[x][i],y=Fa[y][i];
        return Fa[x][0];
    }
     
    char S[2];
     
    bool MOP2;
     
    void _main(void) {
        int T;
        while(~scanf("%d",&T)) {
            int n=Read(),m=Read(),q=Read(),Ans=0;
            rep(i,1,n)root[i]=Sz[i]=Id[i]=0,A[i]=B[i]=Read();
            sort(B+1,B+n+1);
            len=unique(B+1,B+n+1)-B-1;
            rep(i,1,n)A[i]=lower_bound(B+1,B+len+1,A[i])-B;
            rep(i,1,m) {
                int a=Read(),b=Read();
                G.AddEdgepair(a,b);
            }
            rep(i,1,n)if(!Fa[i][0])dfs(i,0,i);
            while(q--) {
                scanf("%s",S);
                int x=Read()^Ans,y=Read()^Ans;
                if(S[0]=='Q') {
                    int k=Read()^Ans,LCA=lca(x,y);
                    Ans=B[tr.query(root[x],root[y],root[Fa[LCA][0]],root[LCA],1,len,k)];
                    printf("%d
    ",Ans);
                } else {
                    G.AddEdgepair(x,y);
                    if(Sz[Id[x]]>Sz[Id[y]])swap(x,y);
                    dfs(x,y,Id[y]);
                }
            }
        }
    }
     
    signed main() {
        _main();
        return 0;
    }
    

    [国家集训队]middle

    这道题才是真正的用到了主席树的本质。(之前的都是区间(k)值)

    这道题若考虑整个序列上的中位数,我们可以二分求解。

    对于当前二分值(x),我们将所有小于它的数都标为(-1),其余的数都标为(1),最后若该序列的(tot>=0)则表示是可行。

    现在让我们拓展到一般的情况,我们可以同样对于每一个二分的值建一颗主席树,同时我们统计答案。

    我们该如何统计答案呢?

    左端点在区间([a,b])中,右端点在区间([c,d])中,所以中间的([b+1,c-1])是必定有的。

    让后如果要让答案最大,那就是让(tot)越大越好,所以我们自然是要维护([a,b])最大的​后缀和以及([c,d])最大的前缀和。

    #include <bits/stdc++.h>
     
    using namespace std;
     
    #define LL long long
    #define reg register
    #define clr(a,b) memset(a,b,sizeof a)
    #define Mod(x) (x>=mod)&&(x-=mod)
    #define debug(x) cerr<<#x<<"="<<x<<endl;
    #define debug2(x,y) cerr<<#x<<"="<<x<<" "<<#y<<"="<<y<<endl;
    #define debug3(x,y,z) cerr<<#x<<"="<<x<<" "<<#y<<"="<<y<<" "<<#z<<"="<<z<<endl;
    #define rep(a,b,c) for(reg int a=(b),a##_end_=(c); a<=a##_end_; ++a)
    #define ret(a,b,c) for(reg int a=(b),a##_end_=(c); a<a##_end_; ++a)
    #define drep(a,b,c) for(reg int a=(b),a##_end_=(c); a>=a##_end_; --a)
    #define erep(i,G,x) for(int i=(G).Head[x]; i; i=(G).Nxt[i])
     
    inline int Read(void) {
        int res=0,f=1;
        char c;
        while(c=getchar(),c<48||c>57)if(c=='-')f=0;
        do res=(res<<3)+(res<<1)+(c^48);
        while(c=getchar(),c>=48&&c<=57);
        return f?res:-res;
    }
     
    template<class T>inline bool Min(T &a, T const&b) {
        return a>b?a=b,1:0;
    }
    template<class T>inline bool Max(T &a, T const&b) {
        return a<b?a=b,1:0;
    }
    const int N=2e4+5,M=8e4+5,mod=1e9+7;
     
    bool MOP1;
     
    int n,B[N],root[N];
     
    struct Function_SEG {
        int cnt,Ls[N*200],Rs[N*200];
        struct node {
            int lsum,rsum,sum;
        } V[N*200];
        void up(int num) {
            V[num].lsum=max(V[Ls[num]].sum+V[Rs[num]].lsum,V[Ls[num]].lsum);
            V[num].rsum=max(V[Rs[num]].sum+V[Ls[num]].rsum,V[Rs[num]].rsum);
            V[num].sum=V[Ls[num]].sum+V[Rs[num]].sum;
        }
        void build(int &Now,int L,int R) {
            Now=++cnt;
            if(L==R) {
                V[Now]=(node)<%1,1,1%>;
                return;
            }
            int mid=(L+R)>>1;
            build(Ls[Now],L,mid);
            build(Rs[Now],mid+1,R);
            up(Now);
        }
        void Insert(int &Now,int pre,int L,int R,int x) {
            Now=++cnt;
            Ls[Now]=Ls[pre],Rs[Now]=Rs[pre];
            if(L==R) {
                V[Now]=(node)<%-1,-1,-1%>;
                return;
            }
            int mid=(L+R)>>1;
            if(x<=mid)Insert(Ls[Now],Ls[pre],L,mid,x);
            else Insert(Rs[Now],Rs[pre],mid+1,R,x);
            up(Now);
        }
        node query(int Now,int L,int R,int l,int r) {
            if(!Now||l>r)return (node)<%0,0,0%>;
            if(L==l&&R==r)return V[Now];
            int mid=(L+R)>>1;
            if(r<=mid)return query(Ls[Now],L,mid,l,r);
            else if(l>mid)return query(Rs[Now],mid+1,R,l,r);
            else {
                node lso=query(Ls[Now],L,mid,l,mid),rso=query(Rs[Now],mid+1,R,mid+1,r),An;
                An.lsum=max(lso.sum+rso.lsum,lso.lsum);
                An.rsum=max(rso.sum+lso.rsum,rso.rsum);
                An.sum=lso.sum+rso.sum;
                return An;
            }
        }
    } tr;
     
    inline bool check(int x,int a,int b,int c,int d) {
        int tot1=tr.query(root[x],1,n,a,b).rsum;
        int tot2=tr.query(root[x],1,n,b+1,c-1).sum;
        int tot3=tr.query(root[x],1,n,c,d).lsum;
        return (tot1+tot2+tot3)>=0;
    }
     
    struct Node {
        int val,pos;
        bool operator<(Node _)const {
            return val<_.val;
        }
    } A[N];
     
    bool MOP2;
     
    void _main(void) {
        n=Read();
        rep(i,1,n)A[i].val=Read(),A[i].pos=i;
        tr.build(root[1],1,n);
        sort(A+1,A+n+1);
        rep(i,2,n)tr.Insert(root[i],root[i-1],1,n,A[i-1].pos);
        int q=Read(),Ans=0;
        while(q--) {
            rep(i,1,4)B[i]=(Read()+Ans)%n+1;
            sort(B+1,B+4+1);
            int L=1,R=n,Res=0;
            while(L<=R) {
                int mid=(L+R)>>1;
                if(check(mid,B[1],B[2],B[3],B[4]))Res=mid,L=mid+1;
                else R=mid-1;
            }
            Ans=A[Res].val;
            printf("%d
    ",Ans);
        }
    }
     
    signed main() {
        _main();
        return 0;
    }
    
  • 相关阅读:
    性能测试工具---loadrunner
    数据库Mysql监控及优化
    数据库基础----Mysql
    常见的性能问题及定位方法
    中间件
    JVM学习篇章(二)
    PhpStorm 配置本地断点调试
    TCP连接 三次握手 四次挥手
    https 的理解
    使用GatewayWorker 开发个即时聊天demo
  • 原文地址:https://www.cnblogs.com/dsjkafdsaf/p/11594629.html
Copyright © 2011-2022 走看看