一棵树,删除一条边,添加一条边,在保证连通的前提下,使最长链最短
o———o———o—–o—–o—-o
Subtr1 Subtr2 …………..Subtri
o代表树的直径的点。subtr指树的直径的点伸出的子树。
断掉的边一定是直径上的边。
枚举其断边,树分成两部分。怎么连边才能使新树最长路最短?
对于新树内所有链,其距离都小于新树直径。如果连的新边在两棵树的直径之间就可以减小最长路了。连哪呢,显然中点最好。
顺序维护Subtr1~Subtri的树的直径,逆序再维护一次。
怎么维护?对于新加的树Merge下原树。新的直径是原树直径两个端点和新树两个端点,这四个点任意两个之间的路径。易证。
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std ;
#define N 300100
int i , j , k , n , m , T , g[N] ;
struct edge {
int y , l ;
}f[N*2] ;
void ins( int x , int y ) {
f[++T].y = y , f[T].l = g[x] , g[x] = T ;
}
struct node {
int x , pr ;
}q[N] ;
bool v[N] ;
int seq[N] , dis[2][N] , t ;
struct point {
int a , dep , bel ;
}X , Y , R;
int bfs( int st , bool typ , bool ti ) {
// Initialize v before Doing this
v[st] = 1 ;
dis[ti][st] = 0 ;
int l = 0 , r = 0 ;
q[++r].x = st ;
q[r].pr = 0 ;
int endpo = 1 , endva = 0 ;
while( l!=r ) {
++l ;
for( int k=g[ q[l].x ] ; k ; k=f[k].l ) if( v[ f[k].y ] == 0 ) {
v[ f[k].y ] = 1 ;
dis[ti][ f[k].y ] = dis[ti][ q[l].x ] + 1 ;
if( dis[ti][ f[k].y ] > endva ) {
endva = dis[ti][ f[k].y ] ;
endpo = r+1 ;
}
q[++r].x = f[k].y , q[r].pr = l ;
}
}
if( typ ) {
t = 0 ;
for( int i = endpo ; i ; i = q[i].pr ) seq[++t] = q[i].x ;
}
for( int i = 1 ; i<=r ; i++ ) v[ q[i].x ] = 0 ;
R.a = q[ endpo ].x , R.dep = dis[0][ q[ endpo ].x ] ;
return q[ endpo ].x ;
}
point now[2] ;
int DD ;
void uppos( point a , point b ) {
if( a.bel==0 || b.bel==0 ) return ;
if( DD<a.dep + b.dep + abs( a.bel - b.bel ) ) {
now[0] = a , now[1] = b , DD = a.dep + b.dep + abs( a.bel - b.bel ) ;
}
}
int ff[N] , d[N] ;
int main() {
scanf("%d",&n ) ;
for( i=1 ; i<n ; i++ ) {
int x , y ;
scanf("%d%d",&x , &y ) ;
ins( x , y ) , ins( y , x ) ;
}
int start = bfs( 1 , 0 , 0 ) ;
bfs( start , 1 , 0 ) ;
for( i=1 ; i<=t ; i++ ) {
if( i!=1 ) v[ seq[i-1] ] = 1 ;
if( i!=t ) v[ seq[i+1] ] = 1 ;
bfs( seq[i] , 0 , 0 ) ;
point A = R ;
bfs( A.a , 0 , 1 ) ;
point B = R ;
A.bel = B.bel = i ;
int D = ( dis[1][B.a] ) ;
if( i!=1 ) v[ seq[i-1] ] = 0 ;
if( i!=t ) v[ seq[i+1] ] = 0 ;
point E = now[0] , Q = now[1] ;
if( i==1 ) {
now[0] = A , now[1] = B ;
DD = D ;
} else {
if( D > DD ) now[0] = A , now[1] = B , DD=D ;
uppos( A , E ) , uppos( B,E ) ;
uppos( A , Q ) , uppos( B,Q ) ;
}
ff[i] = DD ;
}
memset( now , 0 , sizeof now ) ;DD = 0 ;
for( i=t ; i ; i-- ) {
R.a = R.bel = R.dep = 0 ;
if( i!=t ) v[ seq[i+1] ] = 1 ;
if( i!=1 ) v[ seq[i-1] ] = 1 ;
bfs( seq[i] , 0 , 0 ) ;
point A = R ;
bfs( A.a , 0 , 1 ) ;
point B = R ;
A.bel = B.bel = i ;
if( i!=t ) v[ seq[i+1] ] = 0 ;
if( i!=1 ) v[ seq[i-1] ] = 0 ;
int D = ( dis[1][B.a] ) ;
point E = now[1] , Q = now[0] ;
if( i==t ) {
now[0] = A , now[1] = B ;DD = D ;
} else {
if( D>DD ) now[0] = A , now[1] = B , DD = D ;
uppos( E , A ) , uppos( E , B ) ;
uppos( Q , A ) , uppos( Q , B ) ;
}
d[i] = DD ;
}
int ans = 0x7fffffff ;
for( i=1 ; i<t ; i++ ) {
int tmp = max( ff[i]/2+ff[i]%2 + d[i+1]/2+d[i+1]%2 + 1 , ff[i] ) ;
tmp = max( tmp , d[i+1] ) ;
ans = min( ans , tmp ) ;
}
printf("%d",ans ) ;
}
Debuglog
相似代码段容易写错。
本文介绍了一种通过调整树结构来优化最长链长度的算法。该算法通过删除并重新连接特定边来减少树中最长路径的距离,重点介绍了如何利用树的直径特性进行优化,并提供了具体的实现代码。

1594

被折叠的 条评论
为什么被折叠?



