【题解】P3354 [IOI 2005] Riv 河流

P3354】题解

一:【题意】

  • 给定一张带权图,把根节点和其他任意K个点涂成黑色
  • 每个点花费为 自身点权*距离最近黑点祖先长度
  • 求最小总花费

二:【解法】

  • dp[u][i][z]:u子树有i个黑点,u最近黑点祖先(或自身)为z最小花费
  • 枚举dp[u][i][z]和dp[v][j][z']
  • if(z'!=v&&z'!=z) continue;
  • 计算dp[u][i+j][z]

三:【代码】

#include<bits/stdc++.h>
#define Pair pair<int,int>
#define w first
#define to second
#define inf 1e18
#define int long long
using namespace std;
const int N=110,M=50;
vector<Pair> mp[N];int n,K;
int nw[N];
int dis[N][N];
void Dis(int u,int f,int len){
	if(f!=u) dis[f][u]=len;
	for(auto e:mp[u]){
		int v=e.to;
		Dis(v,f,len+e.w);
	}
}
int dp[N][N][N];
int t[N][N];
int lim[N];
void Dp(int u){
	for(auto e:mp[u]){
		int v=e.to;
		Dp(v);
	}
	
	dp[u][1][u]=0;
	for(int i=0;i<=n;i++){
		if(dis[i][u]!=inf) dp[u][0][i]=nw[u]*dis[i][u];
	}
	
	lim[u]=1;
	for(auto e:mp[u]){
		int v=e.to;
		int up=min(K,lim[u]+lim[v]);
		
		for(int i=0;i<=up;i++){
			for(int j=0;j<=n;j++){
				t[i][j]=dp[u][i][j]+dp[v][0][j];
			}
		}
		
		for(int i=0;i<=up;i++){
			for(int j=0;i+j<=up&&j<=lim[v];j++){
				for(int z=0;z<=n;z++){
					for(int z1=0;z1<=n;z1++){
						if(z1!=v&&z1!=z) continue;
						t[i+j][z]=min(t[i+j][z],dp[u][i][z]+dp[v][j][z1]);
					}
				}
			}
		}
		lim[u]=up;
		for(int i=0;i<=lim[u];i++){
			for(int j=0;j<=n;j++){
				dp[u][i][j]=t[i][j];
			}
		}
	}
}
signed main(){
	for(int i=0;i<N;i++){
		for(int j=0;j<N;j++){
			dis[i][j]=inf;
			for(int k=0;k<N;k++){
				dp[i][j][k]=inf;
			}
		}
	}	
	cin>>n>>K;
	K++;
	for(int i=1;i<=n;i++){
		cin>>nw[i];
		int fa,w;cin>>fa>>w;
		mp[fa].push_back({w,i});
	}
	for(int i=0;i<=n;i++) Dis(i,i,0);
	
	Dp(0);
	cout<<dp[0][K][0]<<"\n";
	return 0;
}
//dp[u][i][z]:u子树有i个黑点,u最近黑点祖先(或自身)为z最小花费
//dp[u][i+j][z]
//if(z'!=v&&z'!=z) continue;
//dp[u][i][z]+dp[v][j][z']

//begin:dp[u][1][u]=0,dp[u][0][f]=nw[u]*dis(f,u)
//ans:dp[0][K][0]
//wufu
posted @ 2025-12-17 15:07  Ming3398  阅读(10)  评论(0)    收藏  举报