1. 程式人生 > >Educational Codeforces Round 54 (Rated for Div. 2) E. Vasya and a Tree(dfs+思維)

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

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");

}