CF1076E.Vasya and a Tree|树上差分

CF 1076E. Vasya and a Tree|树上差分

题意

一棵树,它有n个节点,1号节点为根节点,初始所有点的权值为0。

定义以下两个东西:

  • 函数d(i,j)d(i,j) : 指节点iijj所经过边的数量。

  • xx节点的kk级子树,指满足以下条件点的集合:

    ① x为该点的祖先,规定自己也是自己的祖先。

    d(i,j)kd(i,j) \leq k

mm条要求要你来解决:

给出v,d,xv,d,x,将以vv节点的dd级子树的权值加上xx

当处理完所有的要求时,输出所有点的权值。

思路

题目要求对一段深度的某节点的子树进行加和操作,这种操作类似单纯的一维前缀和差分操作,所以可以在树上进行差分操作。

首先就是将所有的操作离线下来,将所有操作挂在节点上。对应下面的代码

vector<vector<pair<int, int>>> q(n + 1);

for(int i = 1; i <= m; i++)
{
    int u, d, v;
    cin >> u >> d >> v;
    q[u].push_back({d, v});
}

接下来的关键点就是如何在树上进行差分操作(对应一维差分 区间左端点的加 和 区间右端点的减)。

因为树中的遍历是基于DFS序的,访问一个子树过后才会访问到另一棵子树

我们在DFS过程中维护差分数组(b[i]b[i]),DFS时携带一个前缀和(sumsum)变量,每访问到一个节点时,先遍历该节点的所有操作,将前缀和(其实就是该路径上差分数组的前缀和,等于当前节点的值)加上操作的值,然后标记差分数组结束的位置(就是在该位置减去操作的那个值)。

当回溯的时候,删除之前打的标记。树遍历是DFS序的,删除之后才会访问到另一棵树相同的深度节点,不会影响b[i]b[i]的值。

b[i]b[i] : 代表深度为ii的节点的标记值

标记:

if(dep[u] + d < n - 1)
    b[dep[u] + d + 1] -= v;//mark

删除标记:

for(auto [d, v] : q[u])
{
    if(dep[u] + d < n - 1)
        b[dep[u] + d + 1] += v;
}

代码

#include<bits/stdc++.h>
using namespace std;
using ll = long long;

int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	
	int n;
	cin >> n;

	vector<vector<int>> g(n + 1);
	vector<int> dep(n + 1);

	for(int i = 1; i < n; i++)
	{
		int u, v;
		cin >> u >> v;
		g[u].push_back(v);
		g[v].push_back(u);
	}

	int m;
	cin >> m;

	vector<vector<pair<int, int>>> q(n + 1);

	for(int i = 1; i <= m; i++)
	{
		int u, d, v;
		cin >> u >> d >> v;
		q[u].push_back({d, v});
	}

	vector<ll> b(3e5 + 1);
	vector<ll> ans(n + 1);
	function<void(int, int, ll)> dfs = [&](int u, int fa, ll sum)
	{
        // 加上标记值
		sum += b[dep[u]]; // minus
        // 枚举节点操作
		for(auto [d, v] : q[u])
		{
			sum += v;//累加
			if(dep[u] + d < n - 1)
				b[dep[u] + d + 1] -= v;//mark
		}
		ans[u] = sum;
		for(auto v : g[u])
		{
			if(v == fa) continue;
			dep[v] = dep[u] + 1;
			dfs(v, u, sum);
		}
        // 删除标记
		for(auto [d, v] : q[u])
		{
			if(dep[u] + d < n - 1)
				b[dep[u] + d + 1] += v;
		}
	};
	dfs(1, -1, 0);
	for(int i = 1; i <= n; i++)
		cout << ans[i] << " \n"[i == n];
	return 0;
}

   转载规则


《CF1076E.Vasya and a Tree|树上差分》 行码棋 采用 知识共享署名 4.0 国际许可协议 进行许可。
 上一篇
nginx知识总结 nginx知识总结
开关nginx Ubuntu下: 打开nginx服务,需要在root权限下(docker容器下也可以用) /etc/init.d/nginx start 重启nginx /sbin/nginx -s reload nginx配置 ht
2022-08-06 2024-02-20
下一篇 
ACM模板 ACM模板
1 杂项 1.1 __int128和快读 inline __int128 read() { __int128 x = 0; int f = 1; char ch; ch = getchar(); while(!isdigit
2022-07-29 2024-02-20
  目录