题意:一棵树有n个节点,1号点为根,初始时所有点点权为0。进行m次操作,每次操作选以x为根的子树,其中与x距离<=y的全部+z。
规模:n,m<=300000。
分析:因为要将一个子树进行加减,同时只有修改没有查询,所以考虑树上差分,每次操作将ans[x]+=z。同时因为距离<=y的才加,所以将距离为y+1的点 i 全部ans[i]-=z。但是这样的点 i 可能有n个,时间复杂度又变成了n^2,所以考虑将所有深度相同的点存到同一个vector中,这样它们的下标是连续的,可以进行区间修改。同时又因为只有修改没有查询,所以再次运用差分(当然区间修改像zqs2020和kzsn巨佬用树状数组或者像Aurora神犇用线段树做都是可以的)。修改距离为y+1的点,也就是在深度为h[x]+y+1的vector数组中查找在x的子树中的点,可以记录下每个点的dfs序,这样就可以在对应深度的vector数组中二分查找第一个in [i]>=in [x]的点 i,c[ i ]-=z;再二分查找第一个ot [j]>ot [x]的点 j,c[ j ]+=z。最后先枚举每个深度的vector数组,用前缀和将差分的c数组回收,然后再把c数组的值加到ans数组上,然后dfs树上前缀和,儿子节点加上父亲节点的ans值即可。
代码(只有1500B。如果用树状数组2000B左右,线段树2500B左右)
#include&lt;bits/stdc++.h&gt;
using namespace std;
int n,m,lst[300003],cntl,h[300003],in[300003],ot[300003],cntt,maxh;
long long ans[300003],c[300003];
struct edge{int nxt,toward;}line[600006];
void add(int x,int y)
{
line[++cntl].nxt=lst[x];
lst[x]=cntl;
line[cntl].toward=y;
return;
}
vector&lt;int&gt;a[300003];
void dfs(int x,int fa)
{
in[x]=++cntt;h[x]=h[fa]+1;
maxh=max(maxh,h[x]);
a[h[x]].push_back(x);
for(int i=lst[x];i;i=line[i].nxt)
if(line[i].toward!=fa)
dfs(line[i].toward,x);
ot[x]=cntt;
return;
}
bool cmpo(int x,int y){return ot[x]&lt;ot[y];}
bool cmpi(int x,int y){return in[x]&lt;in[y];}
void dfss(int x,int fa)
{
ans[x]+=ans[fa]+c[x];
for(int i=lst[x];i;i=line[i].nxt)
if(line[i].toward!=fa)
dfss(line[i].toward,x);
return;
}
int main()
{
scanf(&quot;%d&quot;,&amp;n);
for(int i=1,x,y;i&lt;n;i++) scanf(&quot;%d%d&quot;,&amp;x,&amp;y),add(x,y),add(y,x);
dfs(1,0);
scanf(&quot;%d&quot;,&amp;m);
vector&lt;int&gt;::iterator st,ed;
for(int i=1,x,y,z;i&lt;=m;i++)
{
scanf(&quot;%d%d%d&quot;,&amp;x,&amp;y,&amp;z);
ans[x]+=z;
int hh=min(maxh+1,h[x]+y+1);
if(hh!=maxh+1)
{
ed=upper_bound(a[hh].begin(),a[hh].end(),x,cmpo);
st=lower_bound(a[hh].begin(),a[hh].end(),x,cmpi);
if(ed&gt;st)
{
c[*st]-=z;
if(ed!=a[hh].end()) c[*ed]+=z;
}
}
}
for(int i=1;i&lt;=maxh;i++)
for(st=a[i].begin(),ed=++st,st--;ed!=a[i].end();st++,ed++)
c[*ed]+=c[*st];
dfss(1,0);
for(int i=1;i&lt;=n;i++) printf(&quot;%lld &quot;,ans[i]);
return 0;
}