关于树的直径题

本文介绍了一种通过调整树结构来优化最长链长度的算法。该算法通过删除并重新连接特定边来减少树中最长路径的距离,重点介绍了如何利用树的直径特性进行优化,并提供了具体的实现代码。

一棵树,删除一条边,添加一条边,在保证连通的前提下,使最长链最短

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
相似代码段容易写错。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值