[TJOI2017]城市
[TJOI2017]城市
明明上课听过一模一样的题,不知道为啥第一次写错了,我还是太废了
题解
很容易想到暴力枚举每条边在枚举每个点,O(\(n^3\)),肯定会T,然后我们 想一想如何优化,在改造后直径分为两种情况。(换根dp)
- 直径在被删除的边的两颗子树内。
- 直径经过新连成的边。
两种情况很明显我们要先求出各自在子树中的直径,然后计算出两个子树中某一端点最长路的最小值加上这条边取一个maxl,然后在枚举每一条边的过程中取min{maxl}。
题解
我们需要两次dfs。
dfs1
求出直径maxl和从当前节点往子节点的最大值maxx。
dfs2
我们考虑dp的思路,设从当前节点now延伸出的最长路lmax,从now的父亲fa延伸的最大值famax
则lmax=max(maxx,famax),然后我们需要在过程中处理famax。
对于famax有两种情况。
- 取famax(fa)+val(fa到当前点的权值)
- 取当前点的兄弟节点加上val
对于第二种情况又分为两种情况。
-
当前点事父亲最大值路径上的点,则取父亲的次大值。
-
否则,取父亲的最大值。
标程
#include<bits/stdc++.h> using namespace std; const int MN=5e3+100; const int inf=0x3f3f3f3f; int n,maxl,ans=inf; int a[MN],b[MN],d[MN]; int cnt,head[MN]; struct node{ int nxt,to,w; }e[MN<<1]; inline void add(int a,int b,int w){ e[++cnt].nxt=head[a],head[a]=cnt,e[cnt].to=b,e[cnt].w=w; } struct tree{ int maxx,smax,lmax,famax; }t[MN]; void dfs1(int now,int pre){ for(int i=head[now];i;i=e[i].nxt){ int to=e[i].to,w=e[i].w; if(to==pre)continue; dfs1(to,now); if(t[now].maxx<t[to].maxx+w){ t[now].smax=t[now].maxx; t[now].maxx=t[to].maxx+w; } else{ t[now].smax=max(t[now].smax,t[to].maxx+w); } } maxl=max(maxl,t[now].maxx+t[now].smax); } int dfs2(int now,int pre){ int ret=inf; t[now].lmax=max(t[now].maxx,t[now].famax); for(int i=head[now];i;i=e[i].nxt){ int to=e[i].to,w=e[i].w; if(to==pre)continue; if(w+t[to].maxx==t[now].maxx){ t[to].famax=max(t[now].famax+w,t[now].smax+w); } else{ t[to].famax=max(t[now].famax+w,t[now].maxx+w); } } for(int i=head[now];i;i=e[i].nxt){ int to=e[i].to; if(to==pre)continue; ret=min(ret,dfs2(to,now)); } return min(ret,t[now].lmax); } int main(){ freopen("city.in","r",stdin); freopen("city.out","w",stdout); scanf("%d",&n); for(int i=1;i<n;++i){ scanf("%d%d%d",&a[i],&b[i],&d[i]); add(a[i],b[i],d[i]),add(b[i],a[i],d[i]); } for(int i=1;i<n;++i){ memset(t,0,sizeof(t)); int x=a[i],y=b[i],w=d[i]; maxl=0; dfs1(x,y),dfs1(y,x); ans=min(ans,max(maxl,dfs2(x,y)+dfs2(y,x)+w)); } printf("%d\n",ans); return 0; }

浙公网安备 33010602011771号