zoukankan      html  css  js  c++  java
  • 主席树(可持久化线段树)

    模板介绍

    基础要求

    • 线段树

    • 能灵活运用 线段树

    • 前缀和,差分

    概念

    • 可持久化:在某个历史版本上更改;查询某个历史版本上的值

    引入

    洛谷P3919 可持久化数组

    这道可持久化线段树的题 大概就是单点修改&单点查询

    代码大致就是在线段树的基础上改变她存点的方式

    以前

    tree[k].l == k << 1
    
    tree[k].r == k << 1 | 1
    

    对于可持久化线段树

    tree[k].l != k << 1
    
    tree[k].r != k << 1 | 1
    
    inline int tree_build(int k,int l,int r)
    {
    	k = ++cnt;
        
       每次这样来存点 但l和r仍然保留 表示一个递归和原序列位置的过程
    	if(l == r)
    	{
    		tree[k].val = seq[l];
    		return k;
    	}
    	int mid = l + r >> 1;
    	tree[k].l = tree_build(tree[k].l,l,mid);
    	tree[k].r = tree_build(tree[k].r,mid + 1,r);
    	return k;
    }
    

    大概酱紫

    #include <map>
    #include <cstdio>
    #include <iostream>
    using namespace std;
    #define reg register int
    #define isdigit(x) ('0' <= (x)&&(x) <= '9')
    template<typename T>
    inline T Read(T Type)
    {
    	T x = 0,f = 1;
    	char a = getchar();
    	while(!isdigit(a)) {if(a == '-') f = -1;a = getchar();}
    	while(isdigit(a)) {x = (x << 1) + (x << 3) + (a ^ '0');a = getchar();}
    	return x * f;
    }
    const int MAXN = 1e6 + 10;
    int seq[MAXN],root[MAXN],cnt,tot;
    struct node
    {
    	int l,r,val;
    }tree[MAXN * 13];
    inline int tree_build(int k,int l,int r)
    {
    	k = ++cnt;
    	if(l == r)
    	{
    		tree[k].val = seq[l];
    		return k;
    	}
    	int mid = l + r >> 1;
    	tree[k].l = tree_build(tree[k].l,l,mid);
    	tree[k].r = tree_build(tree[k].r,mid + 1,r);
    	return k;
    }
    inline int update(int k,int l,int r,int pos,int v)
    {
    	tree[++cnt] = tree[k];
    	k = cnt;
    	if(l == r)
    	{
    		tree[k].val = v;
    		return k;
    	}
    	int mid = l + r >> 1;
    	if(pos <= mid) tree[k].l = update(tree[k].l,l,mid,pos,v);
    	else tree[k].r = update(tree[k].r,mid + 1,r,pos,v);
    	return k;
    }
    inline int query(int k,int pos,int l,int r)
    {
    	if(l == r) return tree[k].val;
    	int mid = l + r >> 1;
    	if(pos <= mid) return query(tree[k].l,pos,l,mid);
    	return query(tree[k].r,pos,mid + 1,r);
    }
    int main()
    {
    	int n = Read(1),m = Read(1);
    	for(reg i = 1;i <= n;i++)
    		seq[i] = Read(1);
    	root[tot] = tree_build(1,1,n);
    	while(m--)
    	{
    		int v = Read(1),sit = Read(1),loc = Read(1);
    		if(sit & 1)
    		{
    			int value = Read(1);
    			root[++tot] = update(root[v],1,n,loc,value);
    		} else {
    			int ans = query(root[v],loc,1,n);
    			root[++tot] = root[v];
    			printf("%d
    ",ans);
    		}
    	}
        return 0;
    }
    

    真正的板子题

    针对会主席树的同学

    看看我写的思路 应该就可以秒懂了

    P3834 可持久化线段树 1

    建权值线段树

    如图 每个点的权值为([a,b])的数值个数

    样例输入

    5 5
    25957 6405 15770 26287 26465 
    2 2 1
    3 4 1
    4 5 1
    1 2 2
    4 4 1
    

    数据很大 (−(10^9)(a_i)(10^9))想到离散化

    int len = unique(seq + 1,seq + 1 + n) - seq - 1;
    	for(reg i = 1;i <= n;i++)
    		int it = lower_bound(seq + 1,seq + 1 + len,past_a[i]) - seq;
    

    (Firstly)

    查询思路

    很容易理解 对于一个询问([a,b])

    我们先求([1,b])中有多少个数值

    再求([1,a - 1])中的数值个数

    每一个节点的权值对应相减

    得到的就是([a,b])中的数值个数

    因为我们建立的是权值线段树

    所以一个节点(*p)

    如果(k)(第(k)小值)≤ (*p->val)

    向左子树查找

    反之向右

    直到(l == r)

    找到了第(k)小值(hash)(离散化)后的值

    离散化时 用一个下标(离散化)后的值对应 原值的数组

    就可以输出了

    (Secondly)

    查询要点

    我们先求([1,b])中有多少个数值

    再求([1,a - 1])中的数值个数

    每一个节点的权值对应相减

    这个操作明显复杂且难以实现 时间复杂度也不能保证

    我们便考虑每次(query)时传两个(同时做两个线段树的操作)

    inline int query(int a,int b,int k,int l,int r)
    {
    	if(l == r) return l;
    	int x = tree[tree[a].l].val - tree[tree[b].l].val;
    	int mid = l + r >> 1;
    	if(k <= x) return query(tree[a].l,tree[b].l,k,l,mid);
    	return query(tree[a].r,tree[b].r,k - x,mid + 1,r);
    }
    

    (Thirdly)

    修改

    最开始是空树

    每次添点时把她当做一种新的历史状态

    直接用主席树维护

    (Code)

    #include <map>
    #include <cstdio>
    #include <iostream>
    #include <algorithm>
    using namespace std;
    #define reg register int
    #define isdigit(x) ('0' <= (x)&&(x) <= '9')
    template<typename T>
    inline T Read(T Type)
    {
    	T x = 0,f = 1;
    	char a = getchar();
    	while(!isdigit(a)) {if(a == '-') f = -1;a = getchar();}
    	while(isdigit(a)) {x = (x << 1) + (x << 3) + (a ^ '0');a = getchar();}
    	return x * f;
    }
    const int MAXN = 2e5 + 10;
    int seq[MAXN],root[MAXN << 5],past_a[MAXN],cnt,tot;
    struct node
    {
    	int l,r,val;
    }tree[MAXN << 5];
    inline int tree_build(int k,int l,int r)
    {
    	k = ++cnt;
    	if(l == r) return k;
    	int mid = l + r >> 1;
    	tree[k].l = tree_build(tree[k].l,l,mid);
    	tree[k].r = tree_build(tree[k].r,mid + 1,r);
    	return k;
    }
    inline int update(int k,int l,int r,int pos)
    {
    	tree[++cnt] = tree[k];
    	k = cnt;
    	tree[k].val++;
    	if(l == r) return k;
    	int mid = l + r >> 1;
    	if(pos <= mid) tree[k].l = update(tree[k].l,l,mid,pos);
    	else tree[k].r = update(tree[k].r,mid + 1,r,pos);
    	return k;
    }
    inline int query(int a,int b,int k,int l,int r)
    {
    	if(l == r) return l;
    	int x = tree[tree[a].l].val - tree[tree[b].l].val;
    	int mid = l + r >> 1;
    	if(k <= x) return query(tree[a].l,tree[b].l,k,l,mid);
    	return query(tree[a].r,tree[b].r,k - x,mid + 1,r);
    }
    int main()
    {
    	int n = Read(1),m = Read(1);
    	for(reg i = 1;i <= n;i++)
    		past_a[i] = seq[i] = Read(1);
    	sort(seq + 1,seq + 1 + n);
    	int len = unique(seq + 1,seq + 1 + n) - seq - 1;
    	root[tot] = tree_build(1,1,len);
    	for(reg i = 1;i <= n;i++)
    	{
    		int it = lower_bound(seq + 1,seq + 1 + len,past_a[i]) - seq;
    		root[i] = update(root[i - 1],1,len,it);
    	}
    	while(m--)
    	{
    		int l = Read(1),r = Read(1),k = Read(1);
    		int ans = query(root[r],root[l - 1],k,1,len);
    		printf("%d
    ",seq[ans]);
    	}
        return 0;
    }
    

    习题

    (SP11470 TTM - To the moon)

    (Code)

    #include <map>
    #include <cstdio>
    #include <iostream>
    using namespace std;
    #define reg register int
    #define isdigit(x) ('0' <= (x)&&(x) <= '9')
    template<typename T>
    inline T Read(T Type)
    {
    	T x = 0,f = 1;
    	char a = getchar();
    	while(!isdigit(a)) {if(a == '-') f = -1;a = getchar();}
    	while(isdigit(a)) {x = (x << 1) + (x << 3) + (a ^ '0');a = getchar();}
    	return x * f;
    }
    const int MAXN = 1e5 + 10;
    int seq[MAXN],root[MAXN],cnt,tot;
    typedef long long ll;
    struct node
    {
    	int l,r;
    	ll val,tag;
    }tree[MAXN << 6];
    inline int tree_build(int k,int l,int r)
    {
    	k = ++cnt,tree[k].tag = 0;
    	if(l == r) {tree[k].val = seq[l];return k;}
    	int mid = l + r >> 1;
    	tree[k].l = tree_build(tree[k].l,l,mid);
    	tree[k].r = tree_build(tree[k].r,mid + 1,r);
    	tree[k].val = tree[tree[k].l].val + tree[tree[k].r].val;
    	return k;
    }
    inline int update(int k,int l,int r,int L,int R,ll v)
    {
    	tree[++cnt] = tree[k],k = cnt;
    	tree[k].val += (R - L + 1) * v;
    	if(L == l&&r == R) {tree[k].tag += v;return k;}
    	int mid = l + r >> 1;
    	if(R <= mid) tree[k].l = update(tree[k].l,l,mid,L,R,v);
    	else if(mid < L) tree[k].r = update(tree[k].r,mid + 1,r,L,R,v);
    	else tree[k].l = update(tree[k].l,l,mid,L,mid,v),tree[k].r = update(tree[k].r,mid + 1,r,mid + 1,R,v);
    	return k;
    }
    inline ll query(int k,int l,int r,int L,int R,ll tot)
    {
    	if(l == L&&r == R) return tree[k].val + (r - l + 1) * tot;
    	int mid = l + r >> 1;
    	if(R <= mid) return query(tree[k].l,l,mid,L,R,tot + tree[k].tag);
    	if(mid < L) return query(tree[k].r,mid + 1,r,L,R,tot + tree[k].tag);
    	ll k1 = query(tree[k].l,l,mid,L,mid,tot + tree[k].tag),k2 = query(tree[k].r,mid + 1,r,mid + 1,R,tot + tree[k].tag);
    	return k1 + k2;
    }
    int main()
    {
    	int n = Read(1),m = Read(1),pre = 0;
    	for(reg i = 1;i <= n;i++)
    		seq[i] = Read(1);
    	root[tot] = tree_build(1,1,n);
    	while(m--)
    	{
    		char sit = getchar();
    		while(sit != 'C'&&sit != 'H'&&sit != 'B'&&sit != 'Q') sit = getchar();
    		if(sit == 'C')
    		{
    			int l = Read(1),r = Read(1),d = Read(1);
    			root[pre + 1] = update(root[pre],1,n,l,r,d * 1ll);
    			pre++;
    		} 
    		if(sit == 'Q')
    		{
    			int l = Read(1),r = Read(1);
    			ll ans = query(root[pre],1,n,l,r,0ll);
    			printf("%lld
    ",ans);
    		}
    		if(sit == 'H')
    		{
    			int l = Read(1),r = Read(1),t = Read(1);
    			ll ans = query(root[t],1,n,l,r,0ll);
    			printf("%lld
    ",ans);
    		}
    		if(sit == 'B') pre = Read(1);
    	}
        return 0;
    }
    
  • 相关阅读:
    Ubuntu 装JDK
    U盘文件夹被病毒隐藏,且不能取消解决办法
    wireshark: there are no interfaces on which a capture can be done
    [转]Ubuntu 常用快捷键10个
    恢复被win7覆盖的Ubuntu Grub
    U盘安装Win7 64位
    荣耀3X畅玩版狙击红米note!
    Secret and Whisper
    360 chrome不能登录Google账户
    周鸿祎仍想做手机
  • 原文地址:https://www.cnblogs.com/resftlmuttmotw/p/11677261.html
Copyright © 2011-2022 走看看