[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;
}
寻找真正的热爱

浙公网安备 33010602011771号