Live2D

题解 [APIO2014]连珠线

题目传送门

题目大意

在达芬奇时代,有一个流行的儿童游戏称为连珠线。当然,这个游戏是关于珠子和线的。线是红色或蓝色的,珠子被编号为 \(1\)\(n\)。这个游戏从一个珠子开始,每次会用如下方式添加一个新的珠子:

Append(w, v):一个新的珠子 \(w\) 和一个已经添加的珠子 \(v\) 用红线连接起来。

Insert(w, u, v):一个新的珠子 \(w\) 插入到用红线连起来的两个珠子 \(u,v\) 之间。具体过程是删去 \(u,v\) 之间红线,分别用蓝线连接 \(u,w\)\(w,v\)

每条线都有一个长度。游戏结束后,你的最终得分为蓝线长度之和。

给你连珠线游戏结束后的游戏局面,只告诉了你珠子和链的连接方式以及每条线的长度,没有告诉你每条线分别是什么颜色。

你需要写一个程序来找出最大可能得分。即,在所有以给出的最终局面结束的连珠线游戏中找出那个得分最大的,然后输出最大可能得分。

\(n\le 10^6\)

思路

首先我们通过思考发现这样一个结论:

当树的形态固定的时候,一个点作为中间点的情况当且仅当它的某个儿子连向他,他连向他爸爸

然后我们可以设 \(f_{u,0/1}\) 表示以 \(u\) 为根的子树 \(u\) 这个点是否是中间点的最大贡献。然后我们可以得到转移式:

\[f_{u,0}\to \sum_{v\in son_u} \max\{f_{v,1}+w,f_{u,0}\} \]

\[f_{u,1}\to \max_{v\in son_u} \{f_{u,0}-\max\{f_{v,1}+w,f_{v,0}\}+f_{v,0}+w\} \]

然后答案就是 \(f_{root,0}\)。枚举每个根做一遍 \(\text{dp}\) 可以获得 \(\Theta(n^2) \& 40\) 的好成绩。

然后你发现我们这个东西其实是可以换根的,我们只需要维护一个前缀最大值、后缀最大值即可。具体见代码。

时间复杂度 \(\Theta(n)\)

\(\texttt{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 ('\n'); 
	return 0;
}
/*
dp[u][0]=\sum_{v\in son_u} max{dp[v][0],dp[v][1]+w}
dp[u][1]=dp[u][0]+max_{v\in son_u} (-max(dp[v][0],dp[v][1]+w)+dp[v][0]+w)
*/
posted @ 2020-09-24 16:27  Dark_Romance  阅读(160)  评论(0编辑  收藏  举报