[APIO2014]连珠线 题解

89 篇文章 1 订阅

我们设初始的那个点为root。则所有的蓝色链都是形如 f a t h e r − n o w − s o n father-now-son fathernowson

我们设计两个dp状态: d p i , 0 dp_{i,0} dpi,0表示i号点不作为蓝色链的中间点, d p i , 1 dp_{i,1} dpi,1表示作为中间点。则以下的转移就非常容易了。

d p i , 0 = ∑ max ⁡ ( d p u , 1 + w , d p u , 0 ) dp_{i,0}=\sum \max(dp_{u,1}+w,dp_{u,0}) dpi,0=max(dpu,1+w,dpu,0)

d p i , 1 = ∑ max ⁡ ( d p u , 1 + w , d p u , 0 ) + max ⁡ { d p u , 0 + w − max ⁡ ( d p u , 1 + w , d p u , 0 ) } dp_{i,1}=\sum \max (dp_{u,1}+w,dp_{u,0}) + \max\{dp_{u,0}+w - \max (dp_{u,1}+w,dp_{u,0}) \} dpi,1=max(dpu,1+w,dpu,0)+max{dpu,0+wmax(dpu,1+w,dpu,0)}

换根dp就ok了。

#include<bits/stdc++.h>
#define rb(a,b,c) for(int a=b;a<=c;++a)
#define rl(a,b,c) for(int a=b;a>=c;--a)
#define LL long long
#define IT iterator
#define PB push_back
#define II(a,b) make_pair(a,b)
#define FIR first
#define SEC second
#define FREO freopen("check.out","w",stdout)
#define rep(a,b) for(int a=0;a<b;++a)
#define SRAND mt19937 rng(chrono::steady_clock::now().time_since_epoch().count())
#define random(a) rng()%a
#define ALL(a) a.begin(),a.end()
#define POB pop_back
#define ff fflush(stdout)
#define fastio ios::sync_with_stdio(false)
#define check_min(a,b) a=min(a,b)
#define check_max(a,b) a=max(a,b)
using namespace std;
const int INF=0x3f3f3f3f;
typedef pair<int,int> mp;
/*}
*/
const int MAXN=200000+20;
int n,rest=-2e9,dp[MAXN][2];
vector<mp> g[MAXN];
void prework(int now,int pre){
	int Max=-2e9;
	dp[now][0]=0;
	for(auto it:g[now]){
		int u,w; u=it.FIR,w=it.SEC; 
		if(u==pre) continue;
		prework(u,now);
		dp[now][0]+=max(dp[u][1]+w,dp[u][0]);
		check_max(Max,dp[u][0]+w-max(dp[u][1]+w,dp[u][0]));
	}
	dp[now][1]=Max+dp[now][0];
}
void change_root(int now,int pre){
	vector<mp> V;
	V.PB({-2e9,-1});
	dp[now][0]=0;
	for(auto it:g[now]){
		int u,w; u=it.FIR,w=it.SEC;
		dp[now][0]+=max(dp[u][1]+w,dp[u][0]);
		V.PB({dp[u][0]+w-max(dp[u][1]+w,dp[u][0]),u});
	}
	check_max(rest,dp[now][0]);
	sort(ALL(V)),reverse(ALL(V));
	int old=dp[now][0];
	for(auto it:g[now]){
		int u=it.FIR,w=it.SEC;
		if(u!=pre){
			dp[now][0]=old-max(dp[u][1]+w,dp[u][0]);
			dp[now][1]=dp[now][0]+(V[0].SEC==u? V[1].FIR:V[0].FIR);
			change_root(u,now);
		}
	}
}
int main(){
	scanf("%d",&n);
	rb(i,2,n){
		int a,b,c;
		scanf("%d%d%d",&a,&b,&c);
		g[a].PB({b,c});
		g[b].PB({a,c});
	}
	prework(1,0);
	change_root(1,0);
	cout<<rest<<endl;
	return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值