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