[TJOI2017]城市 樹形dp
阿新 • • 發佈:2020-09-17
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; }