( ext{Solution:})
考虑显然树形(dp).
1.设(dp[i][j])表示子树(i)选了(j)个的最小代价。
发现转移时的(cost)没法转移。
2.设(dp[i][j][k])表示子树(i)选了(k)个,距离(i)最近的伐木场是(j)的最小代价。
不方便处理(i)是不是被选中的情况。
3.设(dp[i][j][k][0/1])表示的是(i)是不是建立的伐木场,其他对应第三种情况。
考虑转移:
首先,计算两点距离可以树上差分,由于我们这里要计算的两点距离都在一条链上,所以(dis=disroot_i-disroot_j.)
对于(i)不选:枚举当前已经选择了多少个,此时对于最近节点(j,vin son_i)的最近节点也必然为(j.)所以要从(dp[v][j]..)状态转移。
对于(i)选:这里(v)最近的就是(x)了。
考虑对当前节点(dp)完后的处理过程:将某些(dp)状态完全计算掉,对于需要把木头运到(j)的要把代价计算上。
#include<bits/stdc++.h>
using namespace std;
const int MAXN=2e3+10;
int n,k,tot,head[MAXN],ans;
struct E{int nxt,to,dis,v;}e[MAXN];
inline void add(int x,int y,int w){
e[++tot]=(E){head[x],y,w};
head[x]=tot;
}
int dp[102][102][51][2],wd[5001];
int dis[500],pa[500],cnt,ac[1001];
void dfs(int x,int fa){
pa[x]=fa;
for(int i=head[x];i;i=e[i].nxt){
int j=e[i].to;
if(j==fa)continue;
dis[j]=dis[x]+e[i].dis;
dfs(j,x);
}
}
//dis[i]表示1-i的距离 预处理dis&pa
void DP(int x){
ac[++cnt]=x;
//dp[i][j][k][0/1] means that the pos i is or not built a base,the nearest pos to i is j,i's tree has been build k
//意味着i及其子树一共建立了k个,距离i最近的是j,i是不是建立了的最小代价
for(int i=head[x];i;i=e[i].nxt){
int v=e[i].to;
if(v==pa[x])continue;
DP(v);
for(int j=tot;j>=1;--j){
int fa=ac[j];
//枚举所有祖先
for(int u=k;u>=0;--u){
//注意倒序更新,因为要从旧状态转移
dp[x][fa][u][0]+=dp[v][fa][0][0];
//当前点没有建立,所以v最近的也是fa
//这里第三维是0 单独处理 即v及其子树没有选
dp[x][fa][u][1]+=dp[v][x][0][0];
//同理 x选了 v最近的就是x了
for(int l=u;l>=0;--l){
dp[x][fa][u][0]=min(dp[x][fa][u][0],dp[v][fa][l][0]+dp[x][fa][u-l][0]);
//对于当前x没选的,那v最近的还是fa,这里dp[x][fa][u-l][0]是v子树之前x选了u-l个
dp[x][fa][u][1]=min(dp[x][fa][u][1],dp[v][x][l][0]+dp[x][fa][u-l][1]);
//对于当前x选;1的,其实就是v最近的改成x,以及记住选择了x即可
//考虑枚举选择了几个
}
}
}
}
for(int i=1;i<=tot;++i){
int fa=ac[i];
for(int j=k;j>=0;--j){
if(j>=1)dp[x][fa][j][0]=min(dp[x][fa][j][0]+wd[x]*(dis[x]-dis[fa]),dp[x][fa][j-1][1]);
else dp[x][fa][j][0]+=wd[x]*(dis[x]-dis[fa]);
//注意合并状态,对于i没建立的要把木头移到j上,建立了的就不需要,但是注意第三维的限制
}
}
cnt--;//当前点已经不会再成为祖先了
}
int main(){
scanf("%d%d",&n,&k);
for(int i=2;i<=n+1;++i){
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
y++;wd[i]=x;//记木材
add(i,y,z);add(y,i,z);
}
dfs(1,0);DP(1);
printf("%d
",dp[1][1][k][0]);
return 0;
}