P3354 [IOI2005] Riv 河流 題解

dolphina發表於2024-06-25

狀態設的很神,設 \(dp[i][j][k]\) 表示以 \(i\) 為根的子樹內, \(i\) 不建伐木場,子樹內有 \(j\) 個伐木場的答案,且往上最近的建了伐木場的祖先為 \(k\) 。設 \(dp2[i][j][k]\) 表示以 \(i\) 為根的子樹內, \(i\) 建伐木場,子樹內有 \(j\) 個伐木場的答案(不包括 \(i\) ),且往上最近的建了伐木場的祖先為 \(k\) 的答案
轉移直接轉,注意要倒序,不然之前的值被更新了,後面的就更新會變多,然後實現有一些細節

#include<bits/stdc++.h>
#define vd void 
const int inf=2000000000;
int gi(){
	char c;int x=0,f=0;
	while(!isdigit(c=getchar()))f|=(c=='-');
	while(isdigit(c))x=(x*10)+(c^48),c=getchar();
	return f?-x:x;
}
template<class T>vd cxk(T&a,T b){a=a>b?a:b;}
template<class T>vd cnk(T&a,T b){a=a<b?a:b;}
struct E{
	int nxt,to,w;
}e[105];
int cnt,hd[105];
int n,K,W[105],dis[105];
int dp[105][55][105],dp2[105][55][105]; //在u放,在u不放
int top,st[105];
vd add(int from,int to,int w){e[++cnt]={hd[from],to,w},hd[from]=cnt;}
vd dfs(int u){
	st[++top]=u;
	for(int t=hd[u];t;t=e[t].nxt){
		int v=e[t].to,w=e[t].w;
		dis[v]=dis[u]+w;dfs(v);
		for(int qwq=1;qwq<=top;qwq++){
			int k=st[qwq];
			for(int i=K;i>=0;i--){
				dp[u][i][k]+=dp[v][0][k],dp2[u][i][k]+=dp[v][0][u];
				for(int j=0;j<=i;j++)
					cnk(dp[u][i][k],dp[u][i-j][k]+dp[v][j][k]),cnk(dp2[u][i][k],dp2[u][i-j][k]+dp[v][j][u]);
			}
		}
	}
	for(int i=1;i<=top;i++){
		int k=st[i];
		for(int j=0;j<=K;j++)dp[u][j][k]=std::min(j||j==K?dp2[u][j-1][k]:inf,dp[u][j][k]+W[u]*(dis[u]-dis[k]));
		//j-1是因為之前dp2裡面沒把u算進去
	}
	--top;
}
int main(){
	n=gi(),K=gi();
	for(int i=1;i<=n;i++){
		int v,d;W[i]=gi();v=gi(),d=gi();
		add(v,i,d);
	}
	dfs(0);
	printf("%d\n",dp[0][K][0]);
	return 0;
}

相關文章