cf Educational Codeforces Round 54 E. Vasya and a Tree

原题:
E. Vasya and a Tree
time limit per test2 seconds
memory limit per test256 megabytes
inputstandard input
outputstandard output
Vasya has a tree consisting of n vertices with root in vertex 1. At first all vertices has 0 written on it.

Let d(i,j) be the distance between vertices i and j, i.e. number of edges in the shortest path from i to j. Also, let’s denote k-subtree of vertex x — set of vertices y such that next two conditions are met:

x is the ancestor of y (each vertex is the ancestor of itself);
d(x,y)≤k.
Vasya needs you to process m queries. The i-th query is a triple vi, di and xi. For each query Vasya adds value xi to each vertex from di-subtree of vi.

Report to Vasya all values, written on vertices of the tree after processing all queries.

Input
The first line contains single integer n (1≤n≤3⋅10^5) — number of vertices in the tree.

Each of next n−1 lines contains two integers x and y (1≤x,y≤n) — edge between vertices x and y. It is guarantied that given graph is a tree.

Next line contains single integer m (1≤m≤3⋅10^5) — number of queries.

Each of next m lines contains three integers vi, di, xi ( 1 ≤ v i ≤ n , 0 ≤ d i ≤ 1 0 9 , 1 ≤ x i ≤ 1 0 9 1≤v_i≤n,0≤d_i≤10^9, 1≤x_i≤10^9 1vin,0di109,1xi109) — description of the i-th query.

Output
Print n integers. The i-th integers is the value, written in the i-th vertex after processing all queries.

Examples
inputCopy
5
1 2
1 3
2 4
2 5
3
1 1 1
2 0 10
4 10 100
output
1 11 1 100 0
input
5
2 3
2 1
5 4
3 4
5
2 0 4
3 10 1
1 2 3
2 3 10
1 1 7
output
10 24 14 11 11
Note
In the first exapmle initial values in vertices are 0,0,0,0,0. After the first query values will be equal to 1,1,1,0,0. After the second query values will be equal to 1,11,1,0,0. After the third query values will be equal to 1,11,1,100,0.

中文:

给你一个树,然后给你m个修改操作,分别为vi,di,xi,表示在节点vi及其以vi起始的深度小于等于di的子节点都加上xi

起始的时候所有节点值都是0,最后让你输出所有节点的值是多少。

代码:

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
const int maxn=3e5+10;

int n, m;
vector<int> D[maxn],X[maxn],G[maxn];
ll add[maxn];
ll ans[maxn];

void dfs(int v, int pre, int h, long long sum)
{
	for(int i=0;i<D[v].size();i++)
    {
		int l = h, r = h + D[v][i];
		add[l] += X[v][i];
		if(r + 1 < maxn)
            add[r + 1] -= X[v][i];
	}
	sum += add[h];//节点位置与深度关系一致,更改单个节点的在一系列查询与修改后的值
	ans[v] = sum;
	for(int i=0;i<G[v].size();i++)
    {
        if(G[v][i] != pre)
			dfs(G[v][i], v, h + 1, sum);
    }

	//回溯后减去对应标记
    for(int i=0;i<D[v].size();i++)
    {
		int l = h, r = h + D[v][i];
		add[l] -= X[v][i];
		if(r + 1 < maxn)
            add[r + 1] += X[v][i];
	}
}
void init(int n)
{
    for(int i=1;i<=n;i++)
    {
        G[i].clear();
        D[i].clear();
        X[i].clear();
    }
    memset(add,0,sizeof(add));
    memset(ans,0,sizeof(ans));
}
int main()
{
    ios::sync_with_stdio(false);
	while(cin>>n)
	{
	    init(n);
	    for(int i = 1; i < n; i++)
        {
            int u, v;
            cin>>u>>v;
            G[u].push_back(v);
            G[v].push_back(u);
        }
        cin>>m;
        for(int i = 1; i <= m; ++i)
        {
            int v, d, x;
            cin>>v>>d>>x;
            D[v].push_back(d);
            X[v].push_back(x);
        }

        dfs(1, 0, 0, 0);
        for(int i = 1; i <= n; ++i)
        {
            if(i!=n)
                cout<<ans[i]<<" ";
            else
                cout<<ans[i]<<endl;
        }
	}
	return 0;
}



解答:

题目中给你的m个操作全部为离线操作,由于时间复杂度的要求,就绝对不能针对每个给定的vi,di,xi进行暴力的修改值。
最原始的想法是,要想出一种办法,在每一次修改的时候,在节点上先标记出来要修改的深度以及修改值的大小,每次涉及到当前节点的修改时使用线段树中的lazy操作,就是延迟标记的方法。只有在最后所有修改完成后,dfs一遍树,将所有值修改完毕,输出结果。

感觉太麻烦,看了官方题解以后,感觉很奇妙。

官方题解当中使用了一个挺冷门的操作,差分数组

大概的功能就是:

有n个数。
m个操作,每一次操作,将x~y区间的所有数增加z;
最后有q个询问,每一次询问求出x~y的区间和。

设a数组表示原始的数组;

d [ i ] = a [ i ] − a [ i − 1 ] ( 1 &lt; i ≤ n , d [ 1 ] = a [ 1 ] ) d[i]=a[i]-a[i-1](1&lt;i≤n,d[1]=a[1]) d[i]=a[i]a[i1](1<in,d[1]=a[1])
f [ i ] = f [ i − 1 ] + d [ i ] ( 1 &lt; i ≤ n , f [ 1 ] = d [ 1 ] = a [ 1 ] ) f[i]=f[i-1]+d[i](1&lt;i≤n,f[1]=d[1]=a[1]) f[i]=f[i1]+d[i](1<in,f[1]=d[1]=a[1])
s u m [ i ] = s u m [ i − 1 ] + f [ i ] ( 1 &lt; i ≤ n , s u m [ 1 ] = f [ 1 ] = d [ 1 ] = a [ 1 ] ) sum[i]=sum[i-1]+f[i](1&lt;i≤n,sum[1]=f[1]=d[1]=a[1]) sum[i]=sum[i1]+f[i](1<in,sum[1]=f[1]=d[1]=a[1])

那么,最后可以得到
s u m [ y ] − s u m [ x − 1 ] = ∑ i = x y a [ i ] sum[y]-sum[x-1]=\sum_{i=x}^y a[i] sum[y]sum[x1]=i=xya[i]

对于一个修改操作,在区间[x,y]上增加z,那么只需要使 d [ x ] + z d[x]+z d[x]+z然后使 d [ y + 1 ] − z d[y+1]-z d[y+1]z即可。

在官方代码中,使用add记录区间的修改值操作,每次给节点赋值以后,在回溯的过程中,在将节点的修改值改回来,以便在其他节点上继续运作即可。

以上公式引用自
https://blog.csdn.net/zsyz_ZZY/article/details/79918809

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值