DIV2:Vasya and a Treetime 树状数组+差分

题目:E.Vasya and a Treetime
time limit per test2 second
smemory limit per test256 megabytes
input:inputstandard
output:outputstandard

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 ii 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 mm queries. The ii-th query is a triple vi, di and xi. For each query Vasya adds value xixi to each vertex from didi-subtree of vivi.
Input
The first line contains single integer nn (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 mm lines contains three integers vi, di, xi (1≤vi≤n, 0≤di≤10^9, 1≤xi≤10^9) — 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
input
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
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

题目大意是给出一颗n个点的树,m个更新操作,每次更新从当前点 vi 往下di深度所有子节点都加上xi。这里vi点的深度加上di 可能会超过树高。
一开始想像线段树那样写个懒人标记一层一层下推,最后访问的时候一次推完所有标记。当时写超时了因为每次在一个点的标记时都要把他原来的标记推下去,或者可叠加就不推,这样毫无疑问无法发挥懒人标记的任何功效(一旦要做标记就得全推下去,直到推到叶子或者某个没做过标记的点为止)。于是开始看题解。学习了一下差分数组这个东西。
差分数组是原数组的相邻两项之差:如f[i]=d[i]-d[i-1];就说f[i]是d[i]的一个 差分数组。
先然f[1]=d[1]-d[0]=d[1]
可以得到 d[i]=f[1]+f[2]+…+f[i] ,即d[i]是f[i]的前缀和;
用差分数组的话 如果要对d 数组某一段区间[left,right]做修改(增加xi)。只需要做f[left]+=xi; f[right+1]-=xi; 因为每一项d[i]都是f[i]的前缀和,只需要在区间头对f[i]加上xi,就可以影响[left,right]内所有d[i]的值,先然left之前的值是不会影响的,但是Right后面的值也会受到影响,在f[right+1]减掉一个xi;就可以去掉对[left,right]后面的值的影响。差分数组适合离线。

这下来这题,因为是m次修改,一次访问,可以用差分数组做。以一棵树的深度来建一个差分数组。因为点权初始是0,所以最后答案就是修改量叠加起来。为了方便求和可以用树状数组建差分数组,这样求和复杂度会降到logn级别,m次求和也就是mlogn,m次修改也是mlogn,绝对不会超时。因为题目说明了1号点是根节点。用dfs从1号点跑一下树的深度就行了,要注意兄弟结点之间的修改量不能叠加,父节点和子节点之间的修改量是可以叠加的,因此每次访问完一个点和他所有子节点,就要清除掉他的修改量(因为接下来会退出这一节点来访问他的兄弟结点)。
贴出代码(链式向前星):
这样建可以不用担心深度超过当前树最大深度问题。因为后面的深度并不影响,而且树状数组复杂度为logn级别。

#include<iostream>
using namespace std;
#include<string.h>
#include<stdio.h>
#define ll long long
#include<vector>
#define fir first
#define sec second
ll c[300010]; //树状数组 
struct EDG{
 int v,nxt;
};
struct ss{
 int deep,value;
};
ss pot[300010];
EDG edg[600010];
vector<ss>vec[300010];
ll ans[300010];
int head[300010];
int cnt;
int n,m;
void init(){
 cnt=0;
 memset(head,-1,sizeof(head));
 memset(c,0,sizeof(c));
 memset(ans,0,sizeof(ans));
}
void addedg(int x,int y){
 edg[cnt].v=y;
 edg[cnt].nxt=head[x];
 head[x]=cnt++;
}
int lowbit(int x){
 return x&-x;
}
void add(int dep1,int dep2,int value){        //差分数组更新 
 for(int i=dep1;i<=300000;i+=lowbit(i)) c[i]+=value;
 for(int i=dep2+1;i<=300000;i+=lowbit(i)) c[i]-=value;
}
ll query(int dep){
 ll sum=0; 
 for(int i=dep;i>=1;i-=lowbit(i)) sum+=c[i];
 return sum;
}
void dfs(int rt,int fa,int dep){
 for(int i=0;i<vec[rt].size();i++) add(dep,dep+vec[rt][i].deep,vec[rt][i].value);
 ans[rt]=query(dep);
 for(int i=head[rt];i+1;i=edg[i].nxt){
  if(edg[i].v!=fa) dfs(edg[i].v,rt,dep+1);
  else continue;
 }
 for(int i=0;i<vec[rt].size();i++) add(dep,dep+vec[rt][i].deep,-vec[rt][i].value);
}
int x,y,p,d,v;
ss cur;
int main(){
 scanf("%d",&n);
 init();
 for(int i=1;i<=n-1;i++){
  scanf("%d%d",&x,&y);
  addedg(x,y);
  addedg(y,x);
 }
 scanf("%d",&m);
 for(int i=1;i<=m;i++){
  scanf("%d%d%d",&p,&d,&v);
  cur.deep=d;
  cur.value=v;
  vec[p].push_back(cur);
 }
 dfs(1,0,1);
 for(int i=1;i<=n;i++){
  printf("%lld ",ans[i]);
 }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值