其他分享
首页 > 其他分享> > 【学习笔记】可持久化权值线段树--主席树 (静态)

【学习笔记】可持久化权值线段树--主席树 (静态)

作者:互联网

001 前置芝士

(1)动态开点线段树

(2)动态开点维护权值线段树

(3)可持久化数组(可持久化线段树)

(4)掌握权值线段树以及查询全局第k大/小值

002 动态开点

我们知道,开一棵线段树数组所需空间为4*MAXN,当MAXN过大时,显然会MLE,那有没有什么优化空间的方法呢?

当然有(废话,要是没有我还写什么

动态开点

顾名思义,就是动态地新开节点。平时使用线段树时,建树过程中直接分成两部分向下递归建树,明显的,这么做会有冗余产生(因为递归时建了某一子节点但可能根本没有用到该节点)。对于这类冗余,我们完全没有必要开这一节点。解决办法很简单,现用现开,没有用的我就不开,用到时在新建节点,可以很大程度上的优化空间复杂度,代码很简单啦qwq

OPEN NODE

void open_node(int &p,int l,int r) //p一定要传址调用
{
    p=++cnt;
    t[p].val=sum[r]-sum[l-1]; //sum类似于前缀和,这里是区间加操作
}

query and update 操作就是在开头加上开点的操作,没什么好说的

动态开点线段树(区间加 区间查询

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
#include<cstdlib>
#include<climits>
#include<algorithm>
#define int long long

using namespace std;

const int maxn=1e6+5;

inline int read()
{
	int w=0,f=1;
	char ch=getchar();
	while(ch<'0' || ch>'9')
	{
		if(ch=='-')
		{
			f=-1;
		}
		ch=getchar();
	}
	while(ch>='0' && ch<='9')
	{
		w=(w<<3)+(w<<1)+(ch^48);
		ch=getchar();
	}
	return w*f;
}

struct s_t
{
	int ls;
	int rs;
	int val;
	int tag;
}t[maxn*4];

int root;

int sum[maxn];

int n,q,cnt;

int a[maxn];

void open_node(int &p,int l,int r)
{
	p=++cnt;
	t[p].val=sum[r]-sum[l-1];
}

void push_up(int p,int l,int r)
{
	if(l==r)
	{
		return ;
	}
	
	int mid=(l+r)>>1;
	
	if(t[p].ls==0)
	{
		open_node(t[p].ls,l,mid);
	}
	
	if(t[p].rs==0)
	{
		open_node(t[p].rs,mid+1,r);
	}
	
	t[p].val=t[t[p].ls].val+t[t[p].rs].val;
}

void push_down(int p,int l,int r)
{
	if(t[p].tag)
	{
		if(l==r)
		{
			return ;
		}
		
		int mid=(l+r)>>1;
	
		if(t[p].ls==0)
		{
			open_node(t[p].ls,l,mid);
		}
	
		if(t[p].rs==0)
		{
			open_node(t[p].rs,mid+1,r);
		}
	
		t[t[p].rs].tag+=t[p].tag;
		t[t[p].ls].tag+=t[p].tag;
		t[t[p].ls].val+=(mid-l+1)*t[p].tag;
		t[t[p].rs].val+=(r-mid)*t[p].tag;
		
		t[p].tag=0;
	}
	
}

void update(int &p,int l,int r,int l_que,int r_que,int k)
{
	if(p==0)
	{
		open_node(p,l,r);
	}
	
	if(l_que<=l && r<=r_que)
	{
		t[p].val+=(r-l+1)*k;
		t[p].tag+=k;
		return ;
	}
	
	push_down(p,l,r);
	
	int mid=(l+r)>>1;
	
	if(l_que<=mid)
	{
		update(t[p].ls,l,mid,l_que,r_que,k);
	}
	
	if(r_que>mid)
	{
		update(t[p].rs,mid+1,r,l_que,r_que,k);
	}
	
	push_up(p,l,r);
}

int query(int &p,int l,int r,int l_que,int r_que)
{
	if(p==0)
	{
		open_node(p,l,r);
	}
	
	if(l_que<=l && r<=r_que)
	{
		return t[p].val;
	}
	
	push_down(p,l,r);
	
	int mid=(l+r)>>1;
	
	int ans=0;
	
	if(l_que<=mid)
	{
		ans+=query(t[p].ls,l,mid,l_que,r_que);
	}
	
	if(r_que>mid)
	{
		ans+=query(t[p].rs,mid+1,r,l_que,r_que);
	}
	
	return ans;
}

signed main()
{
	n=read();
	q=read();
	for(int i=1;i<=n;i++)
	{
		a[i]=read();
		sum[i]=sum[i-1]+a[i];
	}
	
	for(int i=1;i<=q;i++)
	{
		int opt=read();
		
		if(opt==1)
		{
			int l=read();
			int r=read();
			int k=read();
			update(root,1,n,l,r,k);
		}
		
		if(opt==2)
		{
			int l=read();
			int r=read();
			cout<<query(root,1,n,l,r)<<endl;
		}
	}
	
	return 0;
}

标签:rs,--,线段,mid,int,tag,que,include,化权值
来源: https://www.cnblogs.com/SitoASK/p/16699337.html