设 $f[x][i]$ 表示 $x$ 的子树中,离 $x$ 最近的选择的节点距离为 $i$ 的合法方案的最大价值
设 $val[x]$ 表示节点 $x$ 的价值,首先有 $f[x][0]=val[x]$
那么考虑子树的合并,有 $f[x][min(i,j+1)]=max(f[x][min(i,j+1)],f[x][i]+f[v][j])$
注意此时 $f[x][i]$ 不能包括 $v$ 的贡献,这个可以搞个 $tmp$ 存一下新的 $f[x]$,最后统一覆盖掉即可
然后答案就是 $f[rt]$ 里面的状态取个最大值
复杂度 $n^3$
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #include<cmath> #include<vector> using namespace std; typedef long long ll; inline int read() { int x=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); } while(ch>='0'&&ch<='9') { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); } return x*f; } const int N=207; int n,m,val[N]; int fir[N],from[N<<1],to[N<<1],cntt; inline void add(int a,int b) { from[++cntt]=fir[a]; fir[a]=cntt; to[cntt]=b; } int f[N][N],tmp[N],ans; vector <int> son[N]; void dfs(int x,int fa) { f[x][0]=val[x]; for(int i=fir[x];i;i=from[i]) { int &v=to[i]; if(v==fa) continue; dfs(v,x); memset(tmp,0,sizeof(tmp)); for(int j=0;j<=n;j++) for(int k=0;k<=n;k++) if(j+k+1>m) tmp[min(j,k+1)]=max(tmp[min(j,k+1)],f[x][j]+f[v][k]); for(int j=0;j<=n;j++) f[x][j]=tmp[j]; } } int main() { n=read(),m=read(); int a,b; for(int i=1;i<=n;i++) val[i]=read(); if(n==1) { printf("%d ",val[1]); return 0; } for(int i=1;i<n;i++) a=read(),b=read(), add(a,b),add(b,a); dfs(1,0); for(int i=0;i<=n;i++) ans=max(ans,f[1][i]); printf("%d ",ans); return 0; }