1. 程式人生 > 實用技巧 >[TJOI2017]城市 樹形dp

[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;
}