1 2 3 4

[TJOI2017]城市 树形dp

https://www.luogu.com.cn/problem/P3761

这是个神仙题,会卡常

题目让你改一条边把直径变得最短。

 

枚举每条边,会把图分成两个地方,两个连通块(x区和y区域)都换根dp一下,算出离x最远的点的距离记为dis【x】。然后枚举一下

 新直径有三个来源   1 max dis[x]x里面最大的   ,2 dis[y]y里面最大的  ,3 ,min(dis[x] + dis[y] + len)

具体看代码吧,不能用vector会卡常QAQ

 

#include<iostream>
#include<cstring>
#include<cstdio>
#include<vector>
using namespace std;
const int maxn = 5050+11;
typedef long long ll;

struct Node{
	int p;
	ll len;
	int next;
}G[maxn*3];

int head[maxn];
int cnt = 0;
int n;
void add(int x,int y,ll len){
	G[++cnt].p = y;
	G[cnt].len = len;
	G[cnt].next = head[x]; 
	head[x] = cnt;
}

 
ll dp[maxn],dp2[maxn];

int cal(int x,ll val){
	if(val >= dp[x]){
		dp2[x] = dp[x];
		dp[x] = val;
	}
	else{
		if(val >= dp2[x]){
			dp2[x] = val;
		}
	}
	return 0;
}


int dfs(int x,int fa){
	dp[x] = dp2[x] = 0;
	for(int i=head[x];i;i = G[i].next){
		int p = G[i].p;
		ll len = G[i].len;
		if(p == fa) continue;
		dfs(p,x);
		cal(x,dp[p]+len); 
	}
	return 0;
}
ll cns = 1e15;
ll dd;

int dfs2(int x,int fa){
	dd = max(dd,dp[x]);
	cns = min(cns,dp[x]);
	for(int i=head[x];i;i = G[i].next){
		int p = G[i].p;
		ll len = G[i].len;
		if(p == fa) continue;
		if(dp[p] + len == dp[x]){
			cal(p,dp2[x]+len);
		}
		else{
			cal(p,dp[x]+len);
		}
		dfs2(p,x);
	}
	
	return 0;
}

int main(){
	scanf("%d",&n);
	int x,y;
	for(int i=1;i<n;i++){
		int x,y;
		ll len;
		scanf("%d %d %lld",&x,&y,&len);
		add(x,y,len);
		add(y,x,len);
	}
	
	
	ll ans = 1e16;
	for(int i=1;i<=n;i++){
		for(int j=head[i];j;j = G[j].next){
		//	cout<<i<<" "<<G[j].p<<endl;
			int be = i;
			int en = G[j].p;
			ll len = G[j].len;
			if(len > ans) continue;
			if(be > en) continue;
			cns = 1e15;
			dd = 0;
			dfs(be,en);
			dfs2(be,en);
			ll a = cns;
			if(dd > ans || cns + len > ans) continue;
			
			cns = 1e15;
			dfs(en,be);
			dfs2(en,be);
	
			dd = max(dd,cns + a + len);
			ans = min(dd,ans);
		}
	}
	printf("%lld\n",ans);
	return 0;
}

  

posted @ 2020-09-17 12:06  Lesning  阅读(162)  评论(0编辑  收藏  举报