zoukankan      html  css  js  c++  java
  • 可持久化线段树学习笔记

    模板

    #include<bits/stdc++.h>
    using namespace std;
    const int N=2e5+10;
    int cnt,rt[N];
    int n,a[N],t[N],T;
    int ls[N*20],rs[N*20],dat[N*20];
    void Copy(int x,int y){
    	ls[x]=ls[y];
    	rs[x]=rs[y];
    	dat[x]=dat[y]+1;
    	return;
    }
    int build(int l,int r){
    	int p=++cnt;
    	if(l==r)return p;
    	int mid=l+r>>1;
    	ls[p]=build(l,mid);
    	rs[p]=build(mid+1,r);
    	return p;
    }
    int insert(int pre,int l,int r,int x){
    	int p=++cnt;
    	Copy(p,pre);
    	if(l==r)return p;
    	int mid=l+r>>1;
    	if(x<=mid)ls[p]=insert(ls[pre],l,mid,x);
    	else rs[p]=insert(rs[pre],mid+1,r,x);
    	return p;
    }
    int query(int p,int q,int l,int r,int k){
    	if(l==r)return l;
    	int nc=dat[ls[q]]-dat[ls[p]],ret;
    	int mid=l+r>>1;
    	if(nc>=k)ret=query(ls[p],ls[q],l,mid,k);
    	else ret=query(rs[p],rs[q],mid+1,r,k-nc);
    	return ret;
    }
    int main(){
    	scanf("%d%d",&n,&T);
    	for(int i=1;i<=n;i++){
    		scanf("%d",&a[i]);
    		t[i]=a[i];
    	}
    	sort(t+1,t+n+1);
    	int m=unique(t+1,t+n+1)-(t+1);
    	rt[0]=build(1,m);
    	for(int i=1;i<=n;i++){
    		int x=lower_bound(t+1,t+m+1,a[i])-t;
    		rt[i]=insert(rt[i-1],1,m,x);
    	}
    	while(T--){
    		int L,R,K;
    		scanf("%d%d%d",&L,&R,&K);
    		printf("%d\n",t[query(rt[L-1],rt[R],1,m,K)]);
    	}
    	return 0;
    }
    

    可持久化线段树

    共用节点的\(n\)棵线段树。
    可持久化权值线段树也称主席树

    例题

    好了,你现在已经学会主席树的全部内容)),让我们来口胡几道简单题吧。

    题目链接:区间第k大
    全局想必大家都会,权值线段树的板子。
    当询问区间的时候,
    我们建立\(n\)棵权值线段树,第\(i\)棵表示\(1\)~\(i\)的权值线段树。
    利用前缀和的思想,用第\(r\)棵减去第\(l\)-\(1\)棵便能求出\(l\)~\(r\)的权值情况。
    所以我们直接建主席树来表示这\(n\)棵线段树就好了。

    题目链接:[POI2014]KUR-Couriers
    把上一题求答案的函数改改就行。

    题目链接:Dynamic Rankings
    带修改的第\(k\)大,在静态第\(k\)大中的主席树外套个树状数组即可。
    平衡树套线段树也行。

    题目链接:Count on a tree
    树上第\(k\)大。
    注意的两个点:

    1. 结点\(u\)应该在\(fa_u\)的基础上建树。
    2. \(lca\)求距离时是这样的:\(dis_u+dis_v\)\(-\)\(2\times dis_{lca_{u,v}}\),这个题应该是\(dis_u+dis_v\)\(-\) \(dis_{lca_{u,v}}\) \(-\) \(dis_{fa_{lca_{u,v}}}\)


    题目链接:Middle
    不错的题目,不是建权值线段树了,Middle题解

    题目链接:[SDOI2013]森林
    树上第\(k\)大加启发式合并。

    题目链接:CF840D Destiny
    \(luogu\)上黑了。。其实是个\(sb\)题。

    题目链接:[CQOI2015]任务查询系统
    差分一发,然后统计前缀和,线段树合并就好了。

    题目链接:[NOI2010] 超级钢琴
    先求前缀和。
    因为子段长度有\(L\)\(R\)的限制,如果固定左端点\(L\),右端点\(R\)就在一段固定的区间里选择。
    要使\(sum_R-sum_L\)最大,那么\(sum_R\)就应该尽量大。
    有个很常见的二叉堆贪心技巧,就是下面的做法:

    1. 建立一个大根堆,以\(val\)为指标,每个元素是一个三元组\((L,c,val)\)表示以\(L\)为左端点选到了第\(c\)大的右端点,子段值为\(val\)
    2. 先插入每个\((i,1,val)\)
    3. 每次取出堆顶,加上它的\(val\),然后通过\(L\)查询选定区间第\(k+1\)大的\(val'\),然后插入\((L,c+1,val')\),很显然可以主席树。
    4. 执行第\(3\)个步骤\(k\)次。输出结果。
  • 相关阅读:
    房价
    Jsrender初体验
    GCD XOR UVA
    GCD
    Aladdin and the Flying Carpet LightOJ
    HDU6035 2017多校第一场1003 树形DP
    F
    C
    B
    An Easy Physics Problem HDU
  • 原文地址:https://www.cnblogs.com/Xxhdjr/p/14363096.html
Copyright © 2011-2022 走看看