AT_abc187_e

考虑将 \(1\) 号点定为树根,然后通过搜索确定结点之间的父子关系。对于每次操作,先判断该边两端的点的父子关系,然后再分类讨论进行操作。

如何维护每个点的点权呢?注意到,修改有很多次,但查询只在最后有一次,因此可以考虑树上差分。具体地,设 \(a_i\) 表示 \(i\) 的点权,\(f_i\) 表示 \(i\) 的父亲,\(sum_i\) 表示 \(a_i\)\(a_{f_i}\) 的差,对每次操作进行分类讨论。

假设选定边连接的点分别为 \(u\)\(v\),且 \(f_v=u\),则有如下情况:

  • \(v\) 出发。等同于给\(v\) 为根的子树的每个点点权加上 \(k\),将 \(sum_v\) 加上 \(k\) 即可。

  • \(u\) 出发。等同于给除以 \(v\) 为根的子树外的每个点点权加上 \(k\),也可理解为全部点点权加上 \(k\),再给\(v\) 为根的子树的每个点点权减去 \(k\)(相当于没有操作,符合要求)。将 \(sum_1\) 加上 \(k\)\(sum_v\) 减去 \(k\) 即可。

完成全部操作后,再进行一次搜索,通过差分数组求出每个点的点权,输出答案。

时间复杂度 \(O(n+q)\),代码如下:

#include <iostream>
#include <cstdio>
#include <vector>
#define int long long
#define N 1000001

using namespace std;

vector<int> G[N];
int n,m,fa[N],t,nw,k,u[N],v[N],sum[N],ans[N],tot;

void dfs( int nowu )
{
	for( int i = 0 ; i < G[nowu].size() ; i ++ )
	{
		int nowv = G[nowu][i];
		if( !fa[nowv] )
		{
			fa[nowv] = nowu;
			dfs( nowv );
		}
	}
}

void dfsans( int nowu , int s )
{
	ans[nowu] += s;
	for( int i = 0 ; i < G[nowu].size() ; i ++ )
	{
		int nowv = G[nowu][i];
		if( fa[nowv] == nowu )
			dfsans( nowv , s + sum[nowv] );
	}
}

signed main()
{
	int T;
	cin >> n;
	m = n - 1;
	for( int i = 1 ; i <= m ; i ++ )
	{
		cin >> u[i] >> v[i];
		G[u[i]].push_back( v[i] );
		G[v[i]].push_back( u[i] );		
	}
	fa[1] = 1;
	dfs( 1 );
	cin >> T;
	while( T -- )
	{
		cin >> t >> nw >> k;
		if( t == 1 )
		{
			if( fa[u[nw]] == v[nw] ) sum[u[nw]] += k;
			else
			{
				sum[1] += k;
				sum[v[nw]] -= k;
			}
		}
		else
		{
			if( fa[v[nw]] == u[nw] ) sum[v[nw]] += k;
			else
			{
				sum[1] += k;
				sum[u[nw]] -= k;
			}
		}
	}
	dfsans( 1 , sum[1] );
	for( int i = 1 ; i <= n ; i ++ )
		cout << ans[i] << endl;
	return 0;
}
posted @ 2024-01-20 18:15  liyilang2021  阅读(3)  评论(0编辑  收藏  举报