好题
但我不会。
求树上最大权值点集,要求点集中任意两点距离大于 k k k(边长为1)
d p [ u ] [ d e p ] dp[u][dep] dp[u][dep]表示 u u u为根节点的子树中,选择的点集中距离 u u u的深度至少为 d e p dep dep的最大价值。
说实话,第一次见这么设置 d p dp dp的,不过限定了一部分确实方便孩子结点之间处理,
方便处理 ,令 k = k + 1 k=k+1 k=k+1
对于选择 u u u结点的情况,孩子结点为根节点的子树开始就需要都选上,距离为k-1. 对于不选择的情况,就需要孩子结点之间距离大于等于 k k k(此时k已经是k+1了)。
第二种情况的具体计算就是,枚举
d
e
p
dep
dep,对于每个
d
e
p
dep
dep需要有一个点到达这个
d
e
p
dep
dep,其他孩子结点怎么样选择才能满足条件呢,显然是
x
+
1
+
d
e
p
≥
k
x+1+dep≥k
x+1+dep≥k
其他孩子结点为子树的贡献是
d
p
[
s
o
n
]
[
m
a
x
(
d
e
p
−
1
,
k
−
d
e
p
−
1
)
]
dp[son][max(dep-1,k-dep-1)]
dp[son][max(dep−1,k−dep−1)],因为存在满足这个条件但是不超过
d
e
p
−
1
dep-1
dep−1的,而我们要求的是至少。
但这里计算的其实每次都只有当前的,比如至少
x
x
x,只计算了
x
x
x,而没有
x
+
1
x+1
x+1的
所以计算完还需要,反向更新上去。
#include<bits/stdc++.h>
using namespace std;
vector<int> v;
typedef long long ll;
const int maxn = 1e5+5000;
int n,k;
int a[maxn],dp[1050][1050];
vector<int>G[maxn];
void dfs(int u,int f=-1){
dp[u][0]=a[u];
for(auto v:G[u]){
if(v==f)continue;
dfs(v,u);
}
for(int dep=0;dep<n;dep++){
if(dep==0){
for(auto v:G[u]){
if(v==f)continue;
dp[u][dep]+=dp[v][k-1];
}
}
else{
for(auto v:G[u]){
if(v==f)continue;
int cur=dp[v][dep-1];
for(auto other:G[u]){
if(other==v||other==f)continue;
cur+=dp[other][max(dep-1,k-dep-1)];
}
dp[u][dep]=max(dp[u][dep],cur);
}
}
}
for(int dep=n-1;dep>=0;dep--){
dp[u][dep]=max(dp[u][dep],dp[u][dep+1]);
}
}
int main(){
cin>>n>>k;k++;
for(int i=1;i<=n;i++)scanf("%d",a+i);
for(int i=1;i<=n-1;i++){
int a,b;scanf("%d%d",&a,&b);
G[a].push_back(b),G[b].push_back(a);
}
dfs(1);
cout<<dp[1][0]<<endl;
}