传送门
给出一棵
n
n
个点的树,每个点上有头牛,问每个点
k
k
步范围内各有多少头牛。
刚看完题惊了这东西不可做啊。
然后就开始想换根,结果没杠出来。
继续读题发现
k
k
很小啊,才,那这怕不是可以跑一个
O(nk)
O
(
n
k
)
的算法哦。
然后发现确实可以
O(nk)
O
(
n
k
)
做出来,方法是这样的。
我们仍然先选一个节点(为了方便我选的就是
1
1
)当根节点,然后用表示出以每个
p
p
作为根节点的子树中距离不大于
k
k
的节点的权值和,子树之外的节点都先不管。这个东西是可以先通过儿子的信息先转移出每个作为根节点的子树中距离
p
p
等于的节点的权值和,然后再跑一边前缀和求出来的,时间复杂度
O(nk)
O
(
n
k
)
,然后对于每一个点
u
u
,以它作为整个树的根节点时,整棵树的贡献就是沿着到
1
1
的链向上跳,跳到跳不动或者已经跳了步停止,跳的时候用简单的容斥原理统计答案(其实就是累加
siz[fa][k]−siz[p][k−1]
s
i
z
[
f
a
]
[
k
]
−
s
i
z
[
p
]
[
k
−
1
]
),时间复杂度仍然是
O(nk)
O
(
n
k
)
。
代码如下:
#include<bits/stdc++.h>
#define N 100005
using namespace std;
int first[N],n,siz[N][21],cnt=0,dp[N],fa[N],k;
struct Node{int v,next;}e[N<<1];
inline void add(int u,int v){e[++cnt].v=v,e[cnt].next=first[u],first[u]=cnt;}
inline int read(){
int ans=0;
char ch=getchar();
while(!isdigit(ch))ch=getchar();
while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch^48),ch=getchar();
return ans;
}
inline void write(int x){
if(x>9)write(x/10);
putchar((x%10)^48);
}
inline void dfs(int p){
for(int i=first[p];i;i=e[i].next){
int v=e[i].v;
if(v==fa[p])continue;
fa[v]=p;
dfs(v);
for(int j=1;j<=k;++j)siz[p][j]+=siz[v][j-1];
}
}
inline int solve(int p){
int ret=siz[p][k],pos=k-1;
while(fa[p]&&pos!=-1){
ret+=siz[fa[p]][pos]-(pos?siz[p][pos-1]:0);
--pos,p=fa[p];
}
return ret;
}
int main(){
n=read(),k=read();
for(int i=1;i<n;++i){
int u=read(),v=read();
add(u,v),add(v,u);
}
for(int i=1;i<=n;++i)siz[i][0]=read();
dfs(1);
for(int i=1;i<=n;++i)for(int j=1;j<=k;++j)siz[i][j]+=siz[i][j-1];
for(int i=1;i<=n;++i)write(solve(i)),puts("");
return 0;
}