CF1076E Solution
题解
从\(n,m\le 3\cdot 10^5\)可看出,此题需在\(logn\)的时间内进行区间修改与单点查询。而描述操作位置的只有距离(子树深度)一个变量,可以想到以深度为关键字建立线段树或树状数组。
设点\(i\)深度为\(pos_i\),对于每个操作将深度区间\([pos_{v_i},pos_{v_i}+d_i]+x_i\)即可。可以发现,若每个节点在将相关操作区间修改完毕后立刻计算答案,便可保证节点的修改不会影响到其祖先。因此区间修改时可直接将\([1,pos_{v_i}+d_i]+x_i\),这样用树状数组维护更加便捷。立刻计算答案还有另一个原因,为使修改不会影响到同级节点及其子节点,在回溯时需取消每个节点的修改。
AC代码
#include<bits/stdc++.h>
#define lowb(x) x&(-x)
#define int long long
using namespace std;
const int N=3e5+10;
struct node {int v,d,x;} q[N];//操作
int fst[N],nxt[2*N],v[2*N],cnt;
int t[N],ans[N],n;//t:树状数组
vector<int> qwq[N];//qwq[i]:d=i的操作下标
void add(int x,int y)
{
v[++cnt]=y;
nxt[cnt]=fst[x]; fst[x]=cnt;
}
void modif(int x,int y)
{
while(x) {t[x]+=y; x-=lowb(x);}
}
int query(int x)
{
int ans=0;
while(x<=n) {ans+=t[x]; x+=lowb(x);}
return ans;
}
void dfs(int x,int fa,int pos)
{
int siz=qwq[x].size();
for(int i=0;i<siz;i++) modif(min(pos+q[qwq[x][i]].d,n),q[qwq[x][i]].x);//修改操作
//因为pos+d可能会大于n(最大深度),因此需要取min
ans[x]=query(pos);
for(int i=fst[x];i;i=nxt[i])
if(v[i]!=fa) dfs(v[i],x,pos+1);
for(int i=0;i<siz;i++) modif(min(pos+q[qwq[x][i]].d,n),-q[qwq[x][i]].x);//取消修改
}
signed main()
{
int m,x,y;
scanf("%lld",&n);
for(int i=1;i<n;i++)
{
scanf("%lld%lld",&x,&y);
add(x,y); add(y,x);
}
scanf("%lld",&m);
for(int i=1;i<=m;i++)
{
scanf("%lld%lld%lld",&q[i].v,&q[i].d,&q[i].x);
qwq[q[i].v].push_back(i);
}
dfs(1,0,1);
for(int i=1;i<=n;i++) printf("%lld ",ans[i]);
return 0;
}