【洛谷3761】[TJOI2017] 城市(暴枚+换根DP)

点此看题面

  • 给定一棵树,每条边有一个边权。
  • 你可以断开重连一条边(边权不变),求最小化树的直径。
  • (nle5000)

大致思路

刚看完题目完全不知何从下手,结果突然发现数据范围(nle5000)。。。

那不就是直接暴枚断开哪条边就好了吗?

考虑断开一条边把原树分成了两棵树,假设我们重连两点为(a,b),那么树的直径就是两棵树各自的直径(a,b)最远点距离的较大值。

树的直径显然有(O(n))求法,因此我们只需想办法在(O(n))求出到每个点的最大值。

直接换根(DP)就好了。

代码:(O(n^2))

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 5000
#define add(x,y,z) (e[++ee].nxt=lnk[x],e[lnk[x]=ee].to=y,e[ee].v=z)
#define Gmax(x,y) (x<(y)&&(x=(y)))
#define Gmin(x,y) (x>(y)&&(x=(y)))
using namespace std;
int n,k,ee,lnk[N+5];struct edge {int to,nxt,v,g;}e[N<<1];
int Mx[N+5],Sx[N+5],P[N+5];I void dfs1(CI x,CI lst)//第一次dfs,求最大值、次大值
{
	Mx[x]=Sx[x]=P[x]=0;for(RI i=lnk[x],t;i;i=e[i].nxt) e[i].to^lst&&
		(dfs1(e[i].to,x),(t=Mx[e[i].to]+e[i].v)>Mx[x]?(Sx[x]=Mx[x],Mx[x]=t,P[x]=e[i].to):Gmax(Sx[x],t));
	Gmax(k,Mx[x]+Sx[x]);//树的直径
}
int res;I void dfs2(CI x,CI lst,CI s=0)//第二次dfs,换根DP
{
	Gmin(res,max(Mx[x],s));for(RI i=lnk[x];i;i=e[i].nxt)
		e[i].to^lst&&(dfs2(e[i].to,x,max(s,e[i].to^P[x]?Mx[x]:Sx[x])+e[i].v),0);
}
I int Work(CI x,CI y) {return res=1e9,dfs1(x,y),dfs2(x,y),res;}
int main()
{
	RI i,x,y,z;for(scanf("%d",&n),i=1;i^n;++i) scanf("%d%d%d",&x,&y,&z),add(x,y,z),add(y,x,z);
	RI t=1e9;for(i=1;i^n;++i) x=e[2*i-1].to,y=e[2*i].to,k=0,Gmin(t,max(k,Work(x,y)+Work(y,x)+e[2*i].v));//暴枚断哪条边
	return printf("%d
",t),0;
}
原文地址:https://www.cnblogs.com/chenxiaoran666/p/Luogu3761.html