1. 程式人生 > >Codeforces Round #527 F - Tree with Maximum Cost /// 樹形DP

Codeforces Round #527 F - Tree with Maximum Cost /// 樹形DP

題目大意:

給定一棵樹 每個點都有點權 每條邊的長度都為1

樹上一點到另一點的距離為最短路經過的邊的長度總和

樹上一點到另一點的花費為距離乘另一點的點權

選定一點出發 使得其他點到該點的花費總和是最大的

 

先dfs一遍 獲得 s[u] 為u點往下的點權總和(包括u點)

由其子節點v及其本身權值可得 s[u]=s[v]+w[u]

獲得 dp[u] 為u點出發往下的花費總和(u點出發的花費不需要包括u點)

由其子節點v的dp[v]及s[v]可得 dp[u]=dp[v]+s[v]

再深搜一遍樹形dp 獲得 dp[u] 為u點出發到其他所有點的花費總和

此時 fa=1 u=5

dp[fa]=dp[9]=1*(7+10+4)+2*(1+6+5+1)=(1+6+5)*2+(7+4+10)*1+1*2

dp[u]=dp[5]=1*(1+6+5+9)+2*(7+4)+3*1=(1+6+5)*1+9*1+(7+4)*2+1*3

發現由fa的結果得到u的結果需要 加上u往上的值 再減去u往下(包括u)的值

相當於 加上整棵樹的值sum 再減兩次u往下的值s[u] 即dp[u] = dp[fa] + sum 2*s[u]

dp[fa]+sum-2*s[u] =(1+6+5)*2+(7+4+10)*1+1*2 + (1+6+5+7+4+10+9+1) -

2*(10+1+6+5)

        =(1+6+5)*3+(7+4+10)*2+1*3+9 - 2*(10+1+6+5)

        =(1+6+5)*1+(7+4)*2+1*3+9*1 = dp[u]

#include <bits/stdc++.h>
#define LL long long
#define INF 0x3f3f3f3f
using namespace std;
const int N=2e5+5;
LL n, w[N];
LL ans, sum;
LL s[N], dp[N];
// s[u]為u點往下的點的權值總和
vector <int
> E[N]; void dfs2(int u,int fa) { if(fa) dp[u]=dp[fa]+sum-2*s[u]; // 如果u存在父節點 由dp[fa]可得到dp[u] // 此時dp[u]為由u點出發去往其他所有點的總花費 for(int i=0;i<E[u].size();i++) { int v=E[u][i]; if(v==fa) continue; dfs2(v,u); } ans=max(ans,dp[u]); } void dfs1(int u,int fa) { s[u]=w[u]; for(int i=0;i<E[u].size();i++) { int v=E[u][i]; if(v==fa) continue; dfs1(v,u); s[u]+=s[v]; dp[u]=dp[u]+dp[v]+s[v]; // 此時dp[u]為由u點出發往下的總花費 } } int main() { while(~scanf("%I64d",&n)) { ans=sum=0LL; for(int i=1;i<=n;i++) { scanf("%I64d",&w[i]); sum+=w[i]; E[i].clear(); } for(int i=1;i<n;i++) { int u,v; scanf("%d%d",&u,&v); E[u].push_back(v); E[v].push_back(u); } memset(s,0,sizeof(s)); memset(dp,0,sizeof(dp)); dfs1(1,0); dfs2(1,0); printf("%I64d\n",ans); } return 0; }
View Code