题目大意
在达芬奇时代,有一个流行的儿童游戏称为连珠线。当然,这个游戏是关于珠子和线的。线是红色或蓝色的,珠子被编号为 (1) 到 (n)。这个游戏从一个珠子开始,每次会用如下方式添加一个新的珠子:
Append(w, v):一个新的珠子 (w) 和一个已经添加的珠子 (v) 用红线连接起来。
Insert(w, u, v):一个新的珠子 (w) 插入到用红线连起来的两个珠子 (u,v) 之间。具体过程是删去 (u,v) 之间红线,分别用蓝线连接 (u,w) 和 (w,v)。
每条线都有一个长度。游戏结束后,你的最终得分为蓝线长度之和。
给你连珠线游戏结束后的游戏局面,只告诉了你珠子和链的连接方式以及每条线的长度,没有告诉你每条线分别是什么颜色。
你需要写一个程序来找出最大可能得分。即,在所有以给出的最终局面结束的连珠线游戏中找出那个得分最大的,然后输出最大可能得分。
(nle 10^6)
思路
首先我们通过思考发现这样一个结论:
当树的形态固定的时候,一个点作为中间点的情况当且仅当它的某个儿子连向他,他连向他爸爸
然后我们可以设 (f_{u,0/1}) 表示以 (u) 为根的子树 (u) 这个点是否是中间点的最大贡献。然后我们可以得到转移式:
[f_{u,0} o sum_{vin son_u} max{f_{v,1}+w,f_{u,0}}
]
[f_{u,1} o max_{vin son_u} {f_{u,0}-max{f_{v,1}+w,f_{v,0}}+f_{v,0}+w}
]
然后答案就是 (f_{root,0})。枚举每个根做一遍 ( ext{dp}) 可以获得 (Theta(n^2) & 40) 的好成绩。
然后你发现我们这个东西其实是可以换根的,我们只需要维护一个前缀最大值、后缀最大值即可。具体见代码。
时间复杂度 (Theta(n))。
( exttt{Code})
#include <bits/stdc++.h>
using namespace std;
#define Int register int
#define INF 0x3f3f3f3f
#define MAXN 200005
template <typename T> inline void read (T &t){t = 0;char c = getchar();int f = 1;while (c < '0' || c > '9'){if (c == '-') f = -f;c = getchar();}while (c >= '0' && c <= '9'){t = (t << 3) + (t << 1) + c - '0';c = getchar();} t *= f;}
template <typename T,typename ... Args> inline void read (T &t,Args&... args){read (t);read (args...);}
template <typename T> inline void write (T x){if (x < 0){x = -x;putchar ('-');}if (x > 9) write (x / 10);putchar (x % 10 + '0');}
int toop = 1,to[MAXN << 1],wei[MAXN << 1],nxt[MAXN << 1],head[MAXN];
void Add_Edge (int u,int v,int w){
to[++ toop] = v,wei[toop] = w,nxt[toop] = head[u],head[u] = toop;
to[++ toop] = u,wei[toop] = w,nxt[toop] = head[v],head[v] = toop;
}
int n,ans,f[MAXN][2];
vector <int> son[MAXN],len[MAXN];
vector <int> pre[MAXN],suf[MAXN];
void dfs1 (int u,int fa){
f[u][0] = 0,f[u][1] = -INF;
for (Int i = head[u];i;i = nxt[i]) if (to[i] ^ fa) son[u].push_back (to[i]),len[u].push_back (wei[i]);
for (Int i = 0;i < son[u].size();++ i){
int v = son[u][i],w = len[u][i];
dfs1 (v,u),f[u][0] += max (f[v][0],f[v][1] + w);
pre[u].push_back (-max (f[v][0],f[v][1] + w) + f[v][0] + w);
suf[u].push_back (-max (f[v][0],f[v][1] + w) + f[v][0] + w);
}
for (Int i = 0;i < son[u].size();++ i) f[u][1] = max (f[u][1],f[u][0] + pre[u][i]);
for (Int i = 1;i < son[u].size();++ i) pre[u][i] = max (pre[u][i],pre[u][i - 1]);
for (Int i = son[u].size() - 2;i >= 0;-- i) suf[u][i] = max (suf[u][i],suf[u][i + 1]);
}
void dfs2 (int u,int fa,int ff0,int ff1,int ffl){
f[u][0] += max (ff0,ff1 + ffl),f[u][1] += max (ff0,ff1 + ffl);
f[u][1] = max (f[u][1],f[u][0] - max (ff0,ff1 + ffl) + ff0 + ffl);
ans = max (ans,f[u][0]);//以u为根的时候显然不能作为中点
for (Int i = 0;i < son[u].size();++ i){
int v = son[u][i],w = len[u][i];
int newf0 = f[u][0] - max (f[v][0],f[v][1] + w);
int delta = -max (ff0,ff1 + ffl) + ff0 + ffl;
if (i != 0) delta = max (delta,pre[u][i - 1]);
if (i < son[u].size() - 1) delta = max (delta,suf[u][i + 1]);
dfs2 (v,u,newf0,newf0 + delta,w);
}
}
signed main(){
read (n);
for (Int i = 2,u,v,w;i <= n;++ i) read (u,v,w),Add_Edge (u,v,w);
dfs1 (1,0),dfs2 (1,0,0,-INF,-INF);
write (ans),putchar ('
');
return 0;
}
/*
dp[u][0]=sum_{vin son_u} max{dp[v][0],dp[v][1]+w}
dp[u][1]=dp[u][0]+max_{vin son_u} (-max(dp[v][0],dp[v][1]+w)+dp[v][0]+w)
*/