問題への解決策 - [NOIP2007]ツリーネットワークコア(強化版)
この質問は、元のデータあまりにも多くの水で、ssw02トークO(n)のアプローチ
トピックの取り扱い:BZOJ1999
T =(V、E、W)は、通信非環式および有向グラフである設定(また、ツリーの非ルートとして知られている)、各正の整数の右端と、我々は、前記T(treenetwork)ツリーネットワークを呼び出しますVは、Eノードとエッジの集合を表し、Wは、セットの各辺の長さを表し、T、Nのノードをみましょう。パス:ツリーネットワークの任意の2つのノード、一つだけの単純なパスが存在するB、Dで表される(B)において、Bは、パスの各辺の長さであり、これは、パスのエンドポイントの長さです。
我々は、2つのノードBの間の距離である(B)Dを呼び出します。Pに最も近いノードまでの距離のポイントPに点Vからのパス:D(V、P)=分{D(V、U)、Uは、パスP上の接合点です}。ツリーネットワークの直径:ネットワークツリー・パスは、ツリー・ネットワークの最長直径と呼ばれています。所与のツリーネットワークTのために、直径は必ずしも一意ではないが、それは証明できる:各直径の中点は(必ずしも可能棒の内側の側のノードであることを起こるない)ユニークで、我々はこの点を呼び出しますツリー・ネットワークの中心として。
偏心ECC(F):パスの距離に最も遠いノードFからツリーネットワークTパスF、すなわち。タスク:指定されたツリーネットワークT =(V、E、W)について、パスFを求めて非負の整数の、ある(ツリーネットワーク経路のノードで終了している)パスの特定のセグメントに直径、Sを超えていない、その長さ(秒に等しくてもよい)、偏心ECC(F)最小こと。我々は、このコア(コア)のネットワークツリーT =(V、E、W)のパスを呼び出します。必要であれば、Fは、接合部に減少させることができます。
一般に、上記の定義では、コアは、必ずしも唯一ではなく、最小限偏心。次の図は、ツリーネットワークの一例を示しています。図、AB及びACは、2つの直径であり、長さは20でした。Wは、ツリーネットワークの中心点であり、辺の長さが5 EFです。あなたが核S = 11を指定した場合、ウェブパスツリーDEFG、8偏心(パスはDEFと解釈することができます)。あなたが核秒= 0(またはS = 1、S = 2)を指定すると、ツリーは、ネットワークノードF、偏心12です。
入力フォーマット
n行を有する:行1、空白で区切られた2つの正の整数NおよびS、。前記ツリーネットワークのノード数nは、sが結合した木のコアネットワークの長さです。
設定したノード番号は、1、2だった...、N。二行目からn行目には、正の整数3与えられたスペースで区切られた各ラインは、各エッジの両端点のシーケンス番号と長さを示します。例えば、「247」が7にノード2及び4を接続するエッジの長さを表します。
出力形式の
出力は、整数偏心を表します。
問題解決のためのアイデア
基本的な貪欲アルゴリズムはO(N ^ 2)、非常に単純な事実に基づいて、必要がありますし、読者に構築されています
。この質問は半分ではない、ないマルチソース最短シンクではなく、単調なキューを。
限り、我々は実際に木の直径を使い果たしたとして、直径その後、各点(U1、U2、U3、... UT) 、ランDFS2で()、缶の直径を経由せずにポイントの変化を意味します最大距離(Lon_dis [])。そして、それを維持します。
スキャンとトラバーサルのみ。
特定のプロセス:ポインタと同様に、エンド・ノード、モバイルテールエンドノードとテール・ノードから一定の距離を列挙するときのコアの境界の長さ未満です。(尾部は、エンドノードのシフトでシフトするため)。
ACコード:
#include<cstdio>
#include<cstring>
using namespace std;
const int MAXN = 100005 ;
inline int read(){
int s = 0 ; char g=getchar() ;
while( g>'9'||g<'0')g=getchar() ;while( g>='0'&&g<='9')s=s*10+g-'0' , g=getchar() ;
return s ;
}
int tot = 1 , head[ MAXN ] , to[ MAXN*2 ] , nex[ MAXN*2 ] , pre[ MAXN ] , w[ MAXN ] ;
int N , M , ans = 0 , dis[ MAXN ] , lon_dis[ MAXN ], p ;//p记录第一次的最远点
int dia[ MAXN ] , sum[ MAXN ] ;
bool v[ MAXN ] ;
int max( int x , int y ){
if( x > y )return x ;return y ;
}
int min( int x , int y ){
if( x > y)return y ; return x ;
}
void add( int x , int y , int z ){
to[ ++tot ] = y , nex[ tot ] = head[ x ] , w[ tot ] = z , head[ x ] = tot ;
}
void dfs( int u , int fa ){//跑直径
pre[ u ] = fa ;
if( ans < dis[ u ] )ans = dis[ u ] , p = u ;
for( register int i = head[ u ] ; i ; i = nex[ i ] ){
if( to[ i ] == fa )continue ;
dis[ to[ i ] ] = dis[ u ] + w[ i ] ;
dfs( to[ i ] , u ) ;
}
}
void dfs2( int u , int fa ){//跑出单点的lon_dis
if( ans < dis[ u ] )ans = dis[ u ] , p = u ;
for( register int i = head[ u ] ; i ; i = nex[ i ] ){
if( to[ i ] == fa || v[ to[ i ] ] )continue ;
dis[ to[ i ] ] = dis[ u ] + w[ i ] ;
dfs( to[ i ] , u ) ;
}
}
int main(){
N = read() , M = read() ; int m1 , m2 , m3 ;
for( register int i = 1 ; i < N ; ++i ){
m1 = read() , m2 = read() , m3 = read() ;
add( m1 , m2 , m3 ) , add( m2 , m1 , m3 ) ;
}
dfs( 1 , 1 ) ; ans = 0 , dis[ p ] = 0 ;
dfs( p , p ) ; v[ p ] = true ;
m1 = p ;
int num = 1 ;
for( register int i = m1 ; i ; i = pre[ i ] , ++num ){
dia[ num ] = i ;
if( pre[ i ] == i )break ;
for( register int k = head[ i ] ; k ; k = nex[ k ] )
if( to[ k ] == pre[ i ] )sum[ num+1 ] = sum[ num ] + w[ k ] ;//sum是直径距离的前缀和,方便查距离
v[ pre[ i ] ] = true ;
}
ans = 0 , dis[ p ] = 0 ;
dfs2( m1 , m1 ) ; lon_dis[ p ] = dis[ p ] ;
for( register int i = m1 ; i ; i = pre[ i ] ){
ans = 0 , dis[ i ] = 0 , p = 0 ;
dfs2( i , i ),lon_dis[ i ] = dis[ p ] ;
if( pre[ i ] == i )break ;
}
int ECC = (1<<30) , tail = 1 ;//指针往后推
for( register int i = 1 ; i <= num ; ++i ){//写得过丑,请屏蔽
int anns = 0 ;
while( sum[ i ] - sum[ tail ] > M && tail <= num )tail++ ;
if( tail > num )break ;
for( register int j = tail ; j <= i ; ++j )anns = max( anns , lon_dis[ j ] ) ;
anns = max( sum[ tail ]-sum[ 1 ] , max( sum[ num ]-sum[ i ] , anns ) ) ;//前缀和之差为两点距离
ECC = min( anns , ECC ) ;
}
printf("%d",ECC) ;
return 0 ;
}
/*
5 2
1 2 5
2 3 2
2 4 4
2 5 3
*/