连珠线[APIO2014](换根Dp)

文章目录

题目

Luogu
在达芬奇时代,有一个流行的儿童游戏称为连珠线。当然,这个游戏是关于珠子和线的。线是红色或蓝色的,珠子被编号为 1 1 1 n n n。这个游戏从一个珠子开始,每次会用如下方式添加一个新的珠子:
A p p e n d ( w , v ) Append(w, v) Append(w,v):一个新的珠子 w w w 和一个已经添加的珠子 v v v 用红线连接起来。
I n s e r t ( w , u , v ) Insert(w, u, v) Insert(w,u,v):一个新的珠子 w w w 插入到用红线连起来的两个珠子 u , v u,v u,v 之间。具体过程是删去 u , v u,v u,v 之间红线,分别用蓝线连接 u , w u,w u,w w , v w,v w,v
每条线都有一个长度。游戏结束后,你的最终得分为蓝线长度之和。
给你连珠线游戏结束后的游戏局面,只告诉了你珠子和链的连接方式以及每条线的长度,没有告诉你每条线分别是什么颜色。
你需要写一个程序来找出最大可能得分。即,在所有以给出的最终局面结束的连珠线游戏中找出那个得分最大的,然后输出最大可能得分。
1 ≤ N ≤ 2 ⋅ 1 0 5 1\le N\le 2\cdot10^5 1N2105

思路

题目理解了好久。。。
发现没有这样的
在这里插入图片描述
因为我们确定一开始的球之后,只会存在 f a − u − v fa-u-v fauv 的连边
于是可以考虑枚举一开始的球,然后当 f a − u fa-u fau 这条边被占用只有当 u u u 为蓝线中点
于是可以列出 d p dp dp 定义:
f [ u ] [ 0 ] f[u][0] f[u][0] : u u u 子树内 u u u 不作为蓝线中点的最大长度
f [ u ] [ 1 ] f[u][1] f[u][1] : u u u 子树内 u u u 作为蓝线中点的最大长度
注意 f [ u ] [ 1 ] f[u][1] f[u][1] 的答案会在 f a fa fa 那里计算,所以对于根节点是没有 f [ u ] [ 1 ] f[u][1] f[u][1]
考虑转移

f [ u ] [ 0 ] = ∑ v ∈ s o n u m a x ( f [ v ] [ 0 ] , f [ v ] [ 1 ] + w u v ) f[u][0]=\sum_{v\in son_u}max(f[v][0],f[v][1]+w_{uv}) f[u][0]=vsonumax(f[v][0],f[v][1]+wuv)

然后对于 f [ u ] [ 1 ] f[u][1] f[u][1] 我们可以以 f [ u ] [ 0 ] f[u][0] f[u][0] 为基础转移,有:

f [ u ] [ 1 ] = f [ u ] [ 0 ] + m a x ( f [ v ] [ 0 ] + w u v − m a x ( f [ v ] [ 0 ] , f [ v ] [ 1 ] + w u v ) ) f[u][1]=f[u][0]+max(f[v][0]+w_{uv}-max(f[v][0],f[v][1]+w_{uv})) f[u][1]=f[u][0]+max(f[v][0]+wuvmax(f[v][0],f[v][1]+wuv))

也就是减去某个儿子,再加这个新的贡献
但是为了方便转移,我们写成这样

f [ u ] [ 0 ] = ∑ v ∈ s o n u m a x ( f [ v ] [ 0 ] , f [ v ] [ 0 ] + f [ v ] [ 1 ] + w u v ) f[u][0]=\sum_{v\in son_u}max(f[v][0],f[v][0]+f[v][1]+w_{uv}) f[u][0]=vsonumax(f[v][0],f[v][0]+f[v][1]+wuv)

然后对于 f [ u ] [ 1 ] f[u][1] f[u][1] 我们可以以 f [ u ] [ 0 ] f[u][0] f[u][0] 为基础转移,有:

f [ u ] [ 1 ] = m a x ( f [ v ] [ 0 ] + w u v − m a x ( f [ v ] [ 0 ] , f [ v ] [ 0 ] + f [ v ] [ 1 ] + w u v ) ) f[u][1]=max(f[v][0]+w_{uv}-max(f[v][0],f[v][0]+f[v][1]+w_{uv})) f[u][1]=max(f[v][0]+wuvmax(f[v][0],f[v][0]+f[v][1]+wuv))

这样 f [ u ] f[u] f[u] 的两种状态就互不干扰了(不这样好像很难写)
我们的答案就是 f [ r o o t ] [ 0 ] f[root][0] f[root][0]
如果枚举根的话则是 O ( n 2 ) O(n^2) O(n2)
考虑换根 D p Dp Dp
在这里插入图片描述
由于 f [ u ] [ 1 ] f[u][1] f[u][1] f a − u fa-u fau 的答案是在 f a fa fa 时候算的,
我们可以假设根节点也有 f [ u ] [ 1 ] f[u][1] f[u][1] 的状态方便转移
现在 f [ u ] [ 0 ] f[u][0] f[u][0] 是以 u u u 为根的最大值
我们可以先算出 v v v f [ u ] [ 0 ] f[u][0] f[u][0] 的贡献

d = m a x ( f [ v ] [ 0 ] , f [ v ] [ 0 ] + f [ v ] [ 1 ] + w u v ) d=max(f[v][0],f[v][0]+f[v][1]+w_{uv}) d=max(f[v][0],f[v][0]+f[v][1]+wuv)

在考虑以 v v v 为根:

f [ v ] [ 0 ] = f [ v ] [ 0 ] + m a x ( f [ u ] [ 0 ] − d , f [ u ] [ 0 ] − d + f [ u ] [ 1 ] + w u v ) f[v][0]=f[v][0]+max(f[u][0]-d,f[u][0]-d+f[u][1]+w_{uv}) f[v][0]=f[v][0]+max(f[u][0]d,f[u][0]d+f[u][1]+wuv)

f [ v ] [ 1 ] = m a x ( f [ v ] [ 1 ] , f [ u ] [ 0 ] − d + w u v − m a x ( f [ u ] [ 0 ] − d , f [ u ] [ 0 ] − d + f [ u ] [ 1 ] + w u v ) ) f[v][1]=max(f[v][1],f[u][0]-d+w_{uv}-max(f[u][0]-d,f[u][0]-d+f[u][1]+w_{uv})) f[v][1]=max(f[v][1],f[u][0]d+wuvmax(f[u][0]d,f[u][0]d+f[u][1]+wuv))

但发现这里 f [ u ] [ 1 ] f[u][1] f[u][1] 可能是由 v v v 转移过来的
于是要保留最大和次大记为 f [ u ] [ 1 ] f[u][1] f[u][1] f [ u ] [ 2 ] f[u][2] f[u][2]
为了简便我们记

M x = m a x ( f [ u ] [ 0 ] − d , f [ u ] [ 0 ] − d + f [ u ] [ 1 ] + w u v ) Mx=max(f[u][0]-d,f[u][0]-d+f[u][1]+w_{uv}) Mx=max(f[u][0]d,f[u][0]d+f[u][1]+wuv)

则有

f [ v ] [ 1 ] = m a x ( f [ v ] [ 1 ] , f [ u ] [ 0 ] − d + w u v − M x ) f[v][1]=max(f[v][1],f[u][0]-d+w_{uv}-Mx) f[v][1]=max(f[v][1],f[u][0]d+wuvMx)

然后最大值次大值讨论一下即可

#include<set>
#include<map>
#include<stack>
#include<cmath>
#include<queue>
#include<deque>
#include<cstdio>
#include<bitset>
#include<vector>
#include<climits>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define LL long long
#define ULL unsigned long long
int read(){
    int f=1,x=0;char c=getchar();
    while(c<'0'||'9'<c){if(c=='-')f=-1;c=getchar();}
    while('0'<=c&&c<='9') x=(x<<3)+(x<<1)+c-'0',c=getchar();
    return f*x;
}
#define MAXN 200000
#define mp make_pair
#define INF 2000000001
int n;//不为中点 为中点最大/次大转移 为根最大 
int f[MAXN+5][3],g[MAXN+5];
vector<pair<int,int> > G[MAXN+5];
void DFS1(int u,int fa){
	f[u][0]=0,f[u][1]=f[u][2]=-INF;
	for(int i=0;i<(int)G[u].size();i++){
		int v=G[u][i].first,w=G[u][i].second;
		if(v==fa) continue;
		DFS1(v,u);
		int Mx=max(f[v][0],f[v][0]+f[v][1]+w);
		f[u][0]+=Mx;
		if(f[v][0]+w-Mx>f[u][1])
			f[u][2]=f[u][1],f[u][1]=f[v][0]+w-Mx;
		else if(f[v][0]+w-Mx>f[u][2])
			f[u][2]=f[v][0]+w-Mx;
	}
	return ;
}
int ans;
void DFS2(int u,int fa){
	ans=max(ans,f[u][0]);
	for(int i=0;i<(int)G[u].size();i++){
		int v=G[u][i].first,w=G[u][i].second;
		if(v==fa) continue;
		int d=max(f[v][0],f[v][0]+f[v][1]+w),Mx;
		if(f[v][0]+w-d==f[u][1])
			Mx=max(f[u][0]-d,f[u][0]-d+f[u][2]+w);
		else
			Mx=max(f[u][0]-d,f[u][0]-d+f[u][1]+w);
		f[v][0]+=Mx;
		if(f[u][0]-d+w-Mx>f[v][1])
			f[v][2]=f[v][1],f[v][1]=f[u][0]-d+w-Mx;
		else if(f[u][0]-d+w-Mx>f[v][2])
			f[v][2]=f[u][0]-d+w-Mx;
		DFS2(v,u);
	}
	return ;
}
int main(){
	n=read();
	for(int i=1;i<n;i++){
		int u=read(),v=read(),w=read();
		G[u].push_back(mp(v,w)),G[v].push_back(mp(u,w));
	}
	DFS1(1,0);
	//for(int i=1;i<=n;i++)
	//	printf("%d %d %d\n",f[i][0],f[i][1],f[i][2]);
	DFS2(1,0);
	printf("%d\n",ans);
    return 0;
}

思考

这种题换根时候容易被绕晕,可以用这种方式思考:
1.理清变量含义
2.去除 v v v u u u 的贡献
3.尝试将 u u u 改为 v v v 的子树
4.分类讨论转移
注意状态转移时还要尽量让对于同一 u u u 的状态转移互不影响

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值