思路:
考虑选了一个点u就加上他的权值w[u] ,那么对于与u距离小于K的点v,作w[v]-w[u] ,相当于对v造成选u不选v的影响。如果有的点点权变成了负数,那么这个点一定不会被选。如果有的点点权还是正数且之前被w[u]更新了,那就再加上这个点的权值进行“补差”,就相当于选了这个点而没有选u,而如果没有w[u]被更新,则相当于选了 u、v 两个点
然后就是选点顺序的问题,一种可行的方案就是从叶子节点开始,因为叶子节点没有儿子了,而叶子结点对内能影响到的点,父亲也能影响到,从外面一层一层向内收,外面的肯定是处理完了的(子树w[] 为非正),那么对于当前点来说就等同于“叶子”了,所以避免了后效性。
如果从根开始选的的话,比如A 是根,设w[C]<w[A]<w[B],A能影响B、C, 之后dfs找到C,发现w[C]<0 (被w[A] 更新过),则不会选,因为C原来的权值肯定小于A,之后找到B ,w[B]>0,所以会加上 w[B],这是就相当于取了点B,而如果B和C距离大于K了,那么对于一开始选了B来说,C是可选的,而C却在之前已经抛弃了,并且被更新成了负,则会出现后效性;
AC代码
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<int,int> pii;
typedef pair<LL,LL> pLL;
const int N=2e2+5;
#define ls (i<<1)
#define rs (i<<1|1)
#define fi first
#define se second
#define mk make_pair
#define pb push_back
#define mem(a,b) memset(a,b,sizeof(a))
LL read()
{
LL x=0,t=1;
char ch=getchar();
while(!isdigit(ch)){ if(ch=='-')t=-1; ch=getchar(); }
while(isdigit(ch)){ x=10*x+ch-'0'; ch=getchar(); }
return x*t;
}
struct edge
{
int from,to,next;
edge(){}
edge(int ff,int tt,int nn)
{
from=ff; to=tt; next=nn;
}
};
edge e[N<<1];
vector<int> a[N];
int f[N],head[N],w[N],tot,n,k;
void add(int from,int to)
{
e[++tot]=edge(from,to,head[from]);
head[from]=tot;
}
void dfs(int u,int pre,int deep)
{
a[deep].pb(u);
for(int i=head[u];i;i=e[i].next)
if(e[i].to!=pre) dfs(e[i].to,u,deep+1);
}
void dfs2(int u,int pre,int step,int val)
{
w[u]-=val;
if(step==k) return;
for(int i=head[u];i;i=e[i].next)
if(e[i].to!=pre) dfs2(e[i].to,u,step+1,val);
}
int main()
{
n=read(),k=read();
for(int i=1;i<=n;i++) w[i]=read();
for(int i=1;i<n;i++)
{
int x=read(),y=read();
add(x,y); add(y,x);
}
dfs(1,0,1);
int ans=0;
for(int i=n;i;i--)
{
for(int j=0;j<a[i].size();j++)
{
//printf("u=%d deep=%d
",a[i][j],i);
int u=a[i][j];
if(w[u]<=0) continue;
ans+=w[u];
dfs2(u,0,0,w[u]);
}
}
printf("%d
",ans);
return 0;
}