Educational Codeforces Round 54 (Rated for Div. 2) E. Vasya and a Tree(dfs+思维)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_37025443/article/details/84189554

E. Vasya and a Tree

time limit per test

2 seconds

memory limit per test

256 megabytes

input

standard input

output

standard output

Vasya has a tree consisting of nn vertices with root in vertex 11. At first all vertices has 00 written on it.

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

  • xx is the ancestor of yy (each vertex is the ancestor of itself);
  • d(x,y)≤kd(x,y)≤k.

Vasya needs you to process mm queries. The ii-th query is a triple vivi, didi and xixi. For each query Vasya adds value xixi to each vertex from didi-subtree of vivi.

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

Input

The first line contains single integer nn (1≤n≤3⋅1051≤n≤3⋅105) — number of vertices in the tree.

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

Next line contains single integer mm (1≤m≤3⋅1051≤m≤3⋅105) — number of queries.

Each of next mm lines contains three integers vivi, didi, xixi (1≤vi≤n1≤vi≤n, 0≤di≤1090≤di≤109, 1≤xi≤1091≤xi≤109) — description of the ii-th query.

Output

Print nn integers. The ii-th integers is the value, written in the ii-th vertex after processing all queries.

Examples

input

Copy

5
1 2
1 3
2 4
2 5
3
1 1 1
2 0 10
4 10 100

output

Copy

1 11 1 100 0 

input

Copy

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

Copy

10 24 14 11 11 

题意:

给你一颗n个结点的树,然后有m种操作,每一次操作v,d,x——将结点为v的子树中,与v的距离dist<=d的点的权值都+x

最后问你经过所有操作后,每一个点的权值(初始权值全部为0)

解析:

官方的标解比较简单也容易理解,我的做法就比较麻烦。所以先写一下标解,有兴趣的话可以看一下下面我的解法。

To solve this problem we can use a data structure which allows to add some value on segment and get a value from some point (Fenwick tree, segment tree or anything you are familliar with).

Let's run DFS from the root while maintaining current depth. When entering a vertex u on depth h, let's consider all queries having vi=u, and for each such query add xi on segment [h,h+di]. Then for current vertex uu the answer is the value in point h.

When leaving vertex u we need to rollback everything we have done: for all queries having vi=u subtract xi on segment [h,h+di].

 用中文解释一下就是这一个操作的性质其实完全符合dfs序。

我们维护一个inc[i]的数组,inc[i]表示当前状态下,深度为i的点需要加上的权值。

然后这个维护这个数组是有一个性质的更新[a,b]那么我们只需要inc[a]+=x,inc[b+1]-=x,然后inc[i]+=inc[i-1]   i=1...b+1

那么我们问题是不同的子树怎么公用这个数组?很简单,用完之后把自己的痕迹擦掉就可以了。

这个也依赖于dfs序的性质。并且当一棵大子树和一棵在这颗子树内的小子树同时更新时其实时完全可以共存的。

上述情况,当点2更新时我们更新inc中的[2,4],当6更新时我们更新[4,5],8更新时我们更新[5,6],所以这些操作时完全可以共存的。

不能共存的是2和15更新的时候,因为结点2更新是只对自己子树更新的,但是inc[i]又是公共的表示第i层需要加上的值。

所以不同子树之间的操作是不可共存的。那么我们只需要把2操作的痕迹都清楚就可以了,把inc变回成刚进入2时的样子

那么这个方法就是完全可以的 。代码写起来也很简单。

本来我看standing里的大佬还有另外一种相似的解法,只是最后不用dfs,而是通过每一个点的dfs序编号,用循环来遍历

但是里面有些东西没有看懂....不过最简单的做法应该就是标程的做法了。

 这个复杂度应该是O(n+m)

#include <cstdio>
#include <algorithm>
#include <vector>
#include <set>
using namespace std;
const int MAXN = 3e5+100;
typedef long long ll;
typedef struct que
{
    int d;
    int x;
}node;
int n;
vector<int> edge[MAXN];
ll inc[MAXN*2],a[MAXN];
vector<node> query[MAXN];

void dfs1(int x,int d,int fa,ll sum)
{
    for(node j:query[x])
    {
        inc[d]+=j.x;
        if(j.d+d+1<=n) inc[j.d+d+1]-=j.x;
    }
    a[x]=sum+inc[d];
    sum+=inc[d];
    for(int i=0;i<edge[x].size();i++)
    {
        int v=edge[x][i];
        if(v==fa) continue;
        dfs1(v,d+1,x,sum);
    }
    for(node j:query[x])   //清除x对依赖高度的inc[]的影响
    {
        inc[d]-=j.x;
        if(j.d+d+1<=n) inc[j.d+d+1]+=j.x;
    }

}



int main()
{

    scanf("%d",&n);
    for(int i=1;i<n;i++)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        edge[x].push_back(y);
        edge[y].push_back(x);
    }
    int m;
    scanf("%d",&m);
    for(int i=1;i<=m;i++)
    {
        int v,d,x;
        scanf("%d%d%d",&v,&d,&x);
        query[v].push_back(node{d,x});
    }
    dfs1(1,1,0,0);
    for(int i=1;i<=n;i++)
    {
        printf("%lld ",a[i]);
    }

}

下面我自己乱搞的做法,这个做法的来源其实是网络赛的一道题

我的想法其实有点暴力,一个操作v,d,x,其实就是把v的子树的每一个结点+x,然后把v的子树中距离v,d+1距离的点的子树都-x

即,把v的子树内的点都+x,然后把子树中距离>d+1的点都-x,我的核心思想就是这个。

那么提前我们就需要预处理出每一个点的dfs序编号dor[],逆dfs序对应的结点dfor[]

层次遍历()每一层的结点个数ce[],这里注意,我给每一层最后都多预留了一个位置。

inc[i]表示的第i个结点对应减少的值(他专门来处理多加的部分),并且他是bfs序的

因为每一层是相互独立的,但是最后的inc[]我是一起遍历的,这样就涉及到第i层的值递增到第i+1层,

所以增加一个位置,就是把遍历完一层之后值就清空,不会带到下一层。

然后在层次遍历的时候,我们把每层的结点的dfs序编号插入到对应的层的链表中myb[d]

(这里我是用dfs层次遍历的,保证myb[]里的数一定是递增的,就不用sort了)

然后我们需要处理出bor[i],表示第i个结点在inc[]中的下标。(这里本来应该是层次遍历的bfs序,但是每一层都多加 了一个点,

就不是bfs序了)。然后ce[i]也要处理成第i层在inc[i]中的结尾的下标

下面 就是关键代码了。

对于每一次询问,我们先更新v结点,然后我们需要更新距离v,d+1距离的结点。我们需要把他们的结点的值都-x

这个时候就需要用myb[d+1]了,里面放的是第d+1层从左到右的全部结点,我们只需要在里面找到有那一段连续的区间

内的点属于v的子树就可以了。这个时候我们 就可以v的dfs序来二分找出这个区间[left,right)

然后把这些点通过ce[]转换成下标对应更新到inc[]中,这样最后遍历一遍inc[]求出每一个点需要更新的值,

最后再做一遍dfs,把v结点的值a[x]+=inc[bor[x]]一直往下推就可以了

时间复杂度是O(mlogn+n),但是跑出来的时间是与标程差不多的。

#include <cstdio>
#include <algorithm>
#include <vector>
#include <set>
using namespace std;
const int MAXN = 3e5+100;
typedef long long ll;
typedef struct que
{
    int v;
    int d;
    int x;
}node;

vector<int> edge[MAXN];
int dep[MAXN],dor[MAXN],siz[MAXN];
int ce[MAXN],cnt;
int fdor[MAXN];
ll inc[MAXN*2],a[MAXN];
vector<int> myb[MAXN];
int bor[MAXN];
int maxc;

int dfs1(int x,int d,int fa)
{
    dep[x]=d;
    int ans=1;
    dor[x]=cnt;
    fdor[cnt]=x;
    cnt++;
    ce[d]++;
    myb[d].push_back(dor[x]);
    maxc=max(d,maxc);
    for(int i=0;i<edge[x].size();i++)
    {
        int v=edge[x][i];
        if(v==fa) continue;
        ans+=dfs1(v,d+1,x);
    }
    siz[x]=ans;
    return ans;
}


void dfs2(int x,int fa,ll val)
{
    a[x]+=val+inc[bor[x]];
    for(int i=0;i<edge[x].size();i++)
    {
        int v=edge[x][i];
        if(v==fa) continue;
        dfs2(v,x,a[x]);
    }
	
}



int main()
{
    int n;
    scanf("%d",&n);
    maxc=0;
    for(int i=1;i<n;i++)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        edge[x].push_back(y);
        edge[y].push_back(x);
    }
    cnt=1;
    dfs1(1,1,0);
    //bfs(1);
    for(int i=1;i<=maxc+1;i++)
    {
        ce[i]++;
        ce[i]+=ce[i-1];
        for(int j=0;j<myb[i].size();j++)
        {
            bor[fdor[myb[i][j]]]=ce[i-1]+j+1;
        }
    }

    ce[0]=0;
    int m;
    scanf("%d",&m);

    for(int i=1;i<=m;i++)
    {
        int v,d,x;
        scanf("%d%d%d",&v,&d,&x);
        a[v]+=x;
        int ti=dep[v]+d+1;
		if(ti>maxc) continue;
        int left=lower_bound(myb[ti].begin(),myb[ti].end(),dor[v])-myb[ti].begin();
        int right=lower_bound(myb[ti].begin(),myb[ti].end(),dor[v]+siz[v])-myb[ti].begin();
        
        //int left=myb[ti].lower_bound(dor[v])-myb[ti].begin();
        //int right=myb[ti].lower_bound(dor[v]+siz[v]);

        inc[ce[ti-1]+left+1]-=x;
        inc[ce[ti-1]+right+1]+=x;
    }
	for(int i=1;i<=n+maxc+1;i++)
    {
        inc[i]+=inc[i-1];
    }
    dfs2(1,0,0);
    for(int i=1;i<=n;i++)
    {
        if(i==1)printf("%lld",a[i]);
        else printf(" %lld",a[i]);
    }
    printf("\n");

}

猜你喜欢

转载自blog.csdn.net/qq_37025443/article/details/84189554