题面:https://www.luogu.com.cn/problem/P3761
一句话题意:给一棵有边权的树,删一条边并加一条等权边,最小化新树的直径。
题解:
考虑先两边DFS求出树的直径。(不会的请自行百度)
那么我们要删的边一定是直径的某一条边。
证明:如果断的不是直径上的边,那么新直径一定\(\geq\)原直径。
既然题目给了\(n^2\)的时限,我们就可以枚举删的是哪一条边。
现在考虑如何连边。
删边后,我们得到了两棵树。那么新的直径可能有三种情况:
A树上的一条链,B树上的一条链,或是A、B合并后经过新边的一条链。
前两个我们可以\(O(n)\)求出。至于第三个,我们类似找树的重心,
----也就是树上离这个点最远距离最小的点。这个可以两边DFS,
第一次记录下每个点的子树距离最大值和次大值,
第二次换根搞一下就好。
时间复杂度O(\(n^2\))
\(O(n)\)实在太巨了,请翻看其他题解
代码:
#include
using namespace std;
#define re register int
#define F(x,y,z) for(re x=y;x<=z;x++)
#define FOR(x,y,z) for(re x=y;x>=z;x--)
typedef long long ll;
#define I inline void
#define IN inline int
templateI read(D &res){
res=0;register D g=1;register char ch=getchar();
while(!isdigit(ch)){
if(ch=='-')g=-1;
ch=getchar();
}
while(isdigit(ch)){
res=(res<<3)+(res<<1)+(ch^48);
ch=getchar();
}
res*=g;
}
const int INF=1e9+7;
struct E{
int to,nt,w;
}e[10100];
#define T e[k].to
int n,m,tot,X,Y,W,head[5050],ans,mx[2][5050],d[5050],f[5050];
vectorpa;
I add(int x,int y){
e[++tot].to=y;
e[tot].nt=head[x];
head[x]=tot;
e[tot].w=W;
}
I D_1(int x,int fa,int dis){
if(dis>m){m=dis;X=x;}
for(re k=head[x];k!=-1;k=e[k].nt){
if(T==fa||k==W||(k^1)==W)continue;
D_1(T,x,dis+e[k].w);
}
}
I D_2(int x,int fa,int dis){
d[x]=dis;
if(dis>m){m=dis;Y=x;}
for(re k=head[x];k!=-1;k=e[k].nt){
if(T==fa||k==W||(k^1)==W)continue;
D_2(T,x,dis+e[k].w);
}
}
I getpath(int x,int fa){
if(x==Y){m=1;return;}
for(re k=head[x];k!=-1;k=e[k].nt){
if(T==fa||k==W||(k^1)==W)continue;
getpath(T,x);
if(m){
//cout<<"!"<mx[0][x])mx[1][x]=mx[0][x],mx[0][x]=dis;
else if(dis>mx[1][x])mx[1][x]=dis;
}
}
I D_4(int x,int fa,int dis,int s){
if(dis>mx[0][x])mx[1][x]=mx[0][x],mx[0][x]=dis;
else if(dis>mx[1][x])mx[1][x]=dis;
//cout<