大视野1036--树的统计Count(树链剖分)

本文深入探讨了树链剖分与线段树在解决树形问题时的高效方法,详细阐述了如何通过树链剖分确定重链并进行深度优先搜索,以及如何使用线段树维护点权,实现路径最大权和路径权和的快速查询。通过实例演示,读者可以掌握在复杂树形结构中进行操作的技巧。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Description

一棵树上有n个节点,编号分别为1到n,每个节点都有一个权值w。我们将以下面的形式来要求你对这棵树完成一些操作: I. CHANGE u t : 把结点u的权值改为t II. QMAX u v: 询问从点u到点v的路径上的节点的最大权值 III. QSUM u v: 询问从点u到点v的路径上的节点的权值和 注意:从点u到点v的路径上的节点包括u和v本身

Input

输入的第一行为一个整数n,表示节点的个数。接下来n – 1行,每行2个整数a和b,表示节点a和节点b之间有一条边相连。接下来n行,每行一个整数,第i行的整数wi表示节点i的权值。接下来1行,为一个整数q,表示操作的总数。接下来q行,每行一个操作,以“CHANGE u t”或者“QMAX u v”或者“QSUM u v”的形式给出。 对于100%的数据,保证1<=n<=30000,0<=q<=200000;中途操作中保证每个节点的权值w在-30000到30000之间。

Output

对于每个“QMAX”或者“QSUM”的操作,每行输出一个整数表示要求输出的结果。

Sample Input

4
1 2
2 3
4 1
4 2 1 3
12
QMAX 3 4
QMAX 3 3
QMAX 3 2
QMAX 2 3
QSUM 3 4
QSUM 2 1
CHANGE 1 5
QMAX 3 4
CHANGE 3 6
QMAX 3 4
QMAX 2 4
QSUM 3 4

Sample Output

4
1
2
2
10
6
5
6
5
16

树链剖分入门题。涨涨熟练度大笑。树的按链分治,按先重链后轻链的顺序深搜。点权用线段树来维护。这样同条链上的节点在线段树上是一个区间。问题转为多个区间的max和sum.
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
#define maxn 30080
#define maxm 60080
#define lson id<<1,l,mid
#define rson id<<1|1,mid+1,r
int first[maxn],Son[maxn],fa[maxn],Size[maxn],Pos[maxn],pos,Top[maxn],key[maxn],dep[maxn];//Pos表示这个点在线段树中的位置
int vv[maxm],nxt[maxm];
int e;
struct ST
{
	int l,r,sum,maxx;
}st[maxn<<2];
void PushUp(int id)
{
	st[id].sum = st[id<<1].sum + st[id<<1|1].sum;
	st[id].maxx = max(st[id<<1].maxx,st[id<<1|1].maxx);
}
void buildtree(int id,int l,int r)
{
	st[id].l = l,st[id].r = r;
	st[id].maxx = -40000;
	if(l == r)	return;
	int mid = (l+r) >> 1;
	buildtree(lson);
	buildtree(rson);
	PushUp(id);
}
void addedge(int u,int v)
{
	vv[e] = v;	nxt[e] = first[u];	first[u] = e++;
	vv[e] = u;	nxt[e] = first[v];	first[v] = e++;
}

void init()
{
	pos = e = 0;
	dep[1] = 0,fa[1] = 1;
	memset(Son,-1,sizeof(Son));
	memset(first,-1,sizeof(first));
}
void dfs1(int u,int pre)//第一次深搜确定重儿子和size
{
	Size[u] = 1;
	for(int i = first[u];i != -1;i = nxt[i])
	{
		int v = vv[i];
		if(v == pre)	continue;
		dep[v] = dep[u]+1;
		fa[v] = u;
		dfs1(v,u);
		Size[u] += Size[v];
		if(Son[u] == -1 || Size[v] > Size[Son[u]])
			Son[u] = v;
	}
}

void dfs2(int u,int t)
{
	Pos[u] = ++pos;//Pos[u]表示每个点在线段树中的位置
	Top[u] = t;
	if(Son[u] != -1)
		dfs2(Son[u],t);
	for(int i = first[u];i != -1;i = nxt[i])
	{
		int v = vv[i];
		if(v == Son[u] || v == fa[u])	continue;
		dfs2(v,v);
	}
}

void Update(int id,int x,int k)
{
	if(st[id].l == x && st[id].r == x)
	{
		st[id].sum = st[id].maxx = k;
		return;
	}
	if(st[id<<1].r >= x)
		Update(id<<1,x,k);
	else Update(id<<1|1,x,k);
	PushUp(id);
}

int Query(int id,int l,int r,int ope)
{
	if(st[id].l == l && st[id].r == r)
	{
		if(ope)	return st[id].sum;
		else return st[id].maxx;
	}
	if(st[id<<1].r >= r)
		return Query(id<<1,l,r,ope);
	else if(st[id<<1|1].l <= l)
		return Query(id<<1|1,l,r,ope);
	else if(ope)	return Query(id<<1,l,st[id<<1].r,ope) + Query(id<<1|1,st[id<<1|1].l,r,ope);
	else return max(Query(id<<1,l,st[id<<1].r,ope),Query(id<<1|1,st[id<<1|1].l,r,ope));
}

int gaomax(int u,int v)
{
	int f1 = Top[u],f2 = Top[v];
	int ans = -40000;
	while(f1 != f2)
	{
		if(dep[f1] < dep[f2])
		{
			swap(u,v);
			swap(f1,f2);
		}
		ans = max(ans,Query(1,Pos[f1],Pos[u],0));
		u = fa[f1];	f1 = Top[u];
	}
	if(dep[u] > dep[v])	swap(u,v);
	return max(ans,Query(1,Pos[u],Pos[v],0));
}

int gaosum(int u,int v)
{
	int f1 = Top[u],f2 = Top[v];
	int ans = 0;
	while(f1 != f2)
	{
		if(dep[f1] < dep[f2])
		{
			swap(u,v);
			swap(f1,f2);
		}
		ans += Query(1,Pos[f1],Pos[u],1);
		u = fa[f1];	f1 = Top[u];
	}
	if(dep[u] > dep[v])	swap(u,v);
	return ans+Query(1,Pos[u],Pos[v],1);
}


int main()
{
	//freopen("in.txt","r",stdin);
	int n;
	while(scanf("%d",&n)!=EOF)
	{
		init();
		for(int i = 1;i < n;i++)
		{
			int u,v;
			scanf("%d%d",&u,&v);
			addedge(u,v);
		}
		for(int i = 1;i <= n;i++)
			scanf("%d",&key[i]);//key[i]表示每个点的点权
		dfs1(1,1);
		dfs2(1,1);
		buildtree(1,1,n);
		Update(1,0,0);
		for(int i = 1;i <= n;i++)
			Update(1,Pos[i],key[i]);
		int q;
		scanf("%d",&q);
		while(q--)
		{
			char ope[10];
			scanf("%s",ope);
			if(ope[0] == 'C')
			{
				int u,t;
				scanf("%d%d",&u,&t);
				Update(1,Pos[u],t);
			}
			else if(ope[0] == 'Q' && ope[1] == 'M')
			{
				int u,v;
				scanf("%d%d",&u,&v);
				printf("%d\n",gaomax(u,v));
			}
			else 
			{
				int u,v;
				scanf("%d%d",&u,&v);
				printf("%d\n",gaosum(u,v));
			}
		}
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值