洛谷 P2590 树链剖分[线段树 单点修改+区间求和+区间最值]

洛谷 P2590

/*
MJJ's 树链剖分--模板
更新版:引入结构体线段树使得可以进行单点修改与区间最值求解 
  LCA + 线段树 
  
  
  注:此模板缺少区间修改功能!!! 
2022.08.09
*/
#include<bits/stdc++.h>
using namespace std;
#define maxn 5000005
#define inf 900000005
#define ll long long

struct node
{
	int l,r;
	int sum;
	int minn,maxx;
	int lazy;
}tree[maxn<<2];  //结构体线段树

struct node2
{
	int to;
	int next;
}edge[maxn<<1];  //链式前向星存树
int cnt,head[maxn];
inline void add(int u,int v)
{
	edge[++cnt].next = head[u];
	edge[cnt].to = v;
	head[u] = cnt;
}


int dep[maxn];  //x节点的深度 
int fa[maxn];  //x节点的父亲节点 
int son[maxn];  //x节点的重儿子 
int siz[maxn];  //x节点为根的子树大小 

int top[maxn];  //x节点所在链的顶点

int nid[maxn];   //新的节点编号 
int nw[maxn];   //新的节点对应的权重 
int w[maxn];   //初始权值 
int pos; 

inline void DFS1(int now,int fath)
{
	/*传入当前节点和其父亲节点的编号*/
	fa[now] = fath;
	siz[now] = 1;son[now] = 0;
	dep[now] = dep[fath] + 1;
	for(int i=head[now];i;i=edge[i].next)
	{
		if(edge[i].to==fath)
			continue;
		DFS1(edge[i].to,now);
		siz[now] += siz[edge[i].to];
		if(siz[son[now]]<siz[edge[i].to]) //当前now的重儿子不是最大时更新 
			son[now] = edge[i].to;
	}
}
 
inline void DFS2(int now,int topx)
{
	/*先重儿子再轻儿子*/
	top[now] = topx;  //重链的顶端 
	
	/*新增两行代码*/ 
	nid[now] = ++pos;  //新的节点编号 
	nw[pos] = w[now];  //新的权重  
	
	if(son[now])
		DFS2(son[now],topx);  //找重儿子 
	else
		return;
	for(int i=head[now];i;i=edge[i].next)
	{
		if(edge[i].to!=fa[now]&&edge[i].to!=son[now])
			DFS2(edge[i].to,edge[i].to);  //找轻儿子,构造轻链 
	}
}

inline void push_up(int x)
{
	int l = x<<1;
	int r = x<<1|1;
	tree[x].sum = tree[l].sum + tree[r].sum;
	tree[x].minn = min(tree[l].minn,tree[r].minn);
	tree[x].maxx = max(tree[l].maxx,tree[r].maxx);
}
void build(int x,int l,int r)  //建树 
{
	tree[x].lazy = 0;
	tree[x].minn = tree[x].maxx = 0;
	tree[x].l = l;
	tree[x].r = r;
	if(l==r)
	{
		tree[x].sum = nw[l];
		tree[x].maxx = nw[l];
		tree[x].minn = nw[l];
		return;
	}
	int mid = (l+r)>>1;
	build(x<<1,l,mid);
	build(x<<1|1,mid+1,r);
	push_up(x);
	return;
}
inline void tree_change(int x,int l,int r,int q,int v)  //单点修改 
{
	/*
	x:  1  线段树根节点,用于下传标记! 
	l:  1  线段树区间范围 
	r:  n
	q: 修改的节点 
	v: 修改后的值 
	*/
	if(l==r)
	{
		tree[x].sum = tree[x].maxx = tree[x].minn = v;
		return; 
	}
	int mid = (l+r)>>1;
	if(q<=mid) tree_change(x<<1,l,mid,q,v);
	else tree_change(x<<1|1,mid+1,r,q,v);
	push_up(x);
}
inline int querysum(int x,int l,int r)  //区间[l,r]的和 
{
	int ans = 0;
	if(l<=tree[x].l&&r>=tree[x].r)
	{
		return tree[x].sum;
	}
	int mid = (tree[x].l+tree[x].r)>>1;
	if(l<=mid) ans += querysum(x<<1,l,r);
	if(r>mid) ans += querysum(x<<1|1,l,r);
	
	return ans;
}
inline int querymax(int x,int l,int r)  //区间[l,r]的最大值
{
	int ans = -inf;
	if(l<=tree[x].l&&r>=tree[x].r) return tree[x].maxx;
	int mid = (tree[x].l+tree[x].r)>>1;
	if(l<=mid) ans = max(querymax(x<<1,l,r),ans);
	if(r>mid) ans = max(querymax(x<<1|1,l,r),ans);
	
	return ans;
} 
inline int querymin(int x,int l,int r)  //区间[l,r]的最小值 
{
	int ans = inf;
	if(l<=tree[x].l&&r>=tree[x].r) return tree[x].minn;
	int mid = (tree[x].l+tree[x].r)>>1;
	if(l<=mid) ans = min(querymin(x<<1,l,r),ans);
	if(r>mid) ans = min(querymin(x<<1|1,l,r),ans);
	
	return ans;
}
inline int tree_sum(int u,int v)  //求树上节点u到v之和 
{
	int ans = 0;
	while(top[u]!=top[v])
	{
		if(dep[top[u]]<dep[top[v]])
			swap(u,v);
		ans += querysum(1,nid[top[u]],nid[u]);
		u = fa[top[u]];
	}
	if(dep[u]>dep[v])
		swap(u,v);
	ans += querysum(1,nid[u],nid[v]);
	return ans;
}
inline int tree_max(int u,int v)  //求树上节点u到v的最大值 
{
	int ans = -inf;
	while(top[u]!=top[v])
	{
		if(dep[top[u]]<dep[top[v]])
			swap(u,v);
		ans = max(ans,querymax(1,nid[top[u]],nid[u]));
		u = fa[top[u]];
	}
	if(dep[u]>dep[v])
		swap(u,v);
	ans = max(ans,querymax(1,nid[u],nid[v]));
	return ans;
}
inline int tree_min(int u,int v)  //求树上节点u到v的最小值 
{
	int ans = inf;
	while(top[u]!=top[v])
	{
		if(dep[top[u]]<dep[top[v]])
			swap(u,v);
		ans = min(ans,querymin(1,nid[top[u]],nid[u]));
		u = fa[top[u]];
	}
	if(dep[u]>dep[v])
		swap(u,v);
	ans = min(ans,querymin(1,nid[u],nid[v]));
	return ans;
}
int main()
{
	int n,m;
	cin>>n;
	for(int i=1;i<n;++i)
	{
		int u,v;
		cin>>u>>v;
		add(u,v);
		add(v,u);
	}
	for(int i=1;i<=n;++i) cin>>w[i];
	DFS1(1,0);
	DFS2(1,1);
	build(1,1,n);
	cin>>m;
	while(m--)
	{
		char cxk[10];
		cin>>cxk;
		int x,y;
		cin>>x>>y;
		if(cxk[1]=='H')
		{
			tree_change(1,1,n,nid[x],y);
		}
		else if(cxk[1]=='M')
		{
			printf("%d\n",tree_max(x,y));
		}
		else if(cxk[1]=='S')
		{
			printf("%d\n",tree_sum(x,y));
		}
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值