上午看别人的代码,把这题弄懂了
下午开始写,然后肝到现在,恶心死了…
真·写一题学一堆
LOJ#2553传送门
看题可戳传送门
典型猫式数据结构,乱搞拿高分,才不去写什么正解呢哼
行吧,这道题呢,我们把一对点在这道题中的「距离」用语言描述出来,就是 T T 上 u,v u , v 到根的链并,减去 T′ T ′ 上 u,v u , v 的LCA深度
那么一个很显然(并不)的思路就是,在 T′ T ′ 上枚举LCA, u,v u , v 分居不同子树,这个比较经典的模型,一般都是用可合并的数据结构维护,顺便计算贡献
但是启发式合并是GG的,首先启发式合并自带一个 logn log n 。启发式合并后,还剩下的问题是「 u u 和某个点集中的点 v v 形成的链并,权值最大是多少」。可以把贡献拆成 v v 到LCA的权值和 + u u 到根的权值和,然后用树剖统计,这样又有了 log2n log 2 n ,不太可过(附注:关于贡献维护,v到LCA可看作dep[v]-dep[LCA],于是每个节点存 -dep[LCA]最大值(这是个常量),已加入的dep[v]最大值,以及dep[v]-dep[LCA]最大值即可。此方法是me临时yy的,如果存在问题的话,请dalao指正)
所以我们需要换一种数据结构来维护。统计链的信息,还可以使用点分治。在这道题中,需要的是和别的子树内的点成链,还需要动态加入,所以复杂度是与点度相关的,还是不ok
因此我们使用边分治!边分治的话,因为一次只会切掉一条边,从而最后一定是一个二叉树,维护很方便
另外,因为边分治一次只能切一条边,如果原图是个菊花,复杂度就不对了。为了解决这个问题,我们可以强行向图里塞一堆点,把原树直接改造成一个二叉树,并且让这些点与周围点连边的边权都是0,这样也不会影响答案
然后关于动态加点,我们需要先把这个边分树建出来,之后加点的时候才知道应该放在什么位置。(线段树的动态加点就不需要提前建一颗树,因为我们可以通过 L≤mid L ≤ m i d 或者 R>mid R > m i d 的判断来进行及时定位,从而知道需不需要向左/右走。然而点/边分是不行的,及时定位的代价太高)
关于实现细节,还是看代码叭…反正挺多的me已经说不清楚了
#include
#include
#include
#include
using namespace std ;
int N , vcnt ;
long long toRoot[750005] ;
struct Path{
int to , len ;
Path( int t = 0 , int l = 0 ): to( t ) , len( l ) {} ;
} son[750000] ;
vector p1[750000] , p2[750000] ;
void In( int t1 , int t2 , int t3 , vector *p ){
p[t1].push_back( Path( t2 , t3 ) ) ;
p[t2].push_back( Path( t1 , t3 ) ) ;
}
template <typename T>
void smax( T &x , T y ){ if( x < y ) x = y ; }
void dfs_rebuild( int u , int f ){
int ba = 0 , fr = 1 ;
for( vector ::iterator it = p1[u].begin() ; it != p1[u].end() ; it ++ ){
int v = it->to ; if( v == f ) continue ;
toRoot[v] = toRoot[u] + it->len ;
dfs_rebuild( v , u ) ;
} for( vector ::iterator it = p1[u].begin() ; it != p1[u].end() ; it ++ )
if( it->to != f ) son[++ba] = *it ;
while( ba - fr >= 2 ){ // 3 or more
Path s1 = son[fr++] , s2 = son[fr++] ;
int w = ++ vcnt ; son[++ba] = Path( w , 0 ) ;
In( w , s1.to , s1.len , p1 ) ;
In( w , s2.to , s2.len , p1 ) ;
} p1[u].clear() ;
while( ba >= fr ) In( u , son[fr].to , son[fr].len , p1 ) , fr ++ ;
}
int ls[1500000] , rs[1500000] , fa[1500000] ;
int dif[750000] , siz[750000] , SIZ , dep[750000] , val[1500000] ;
long long dist[25][750000] ;
void dfs_getdis( int u , int f , int dep ){
for( vector ::iterator it = p1[u].begin() ; it != p1[u].end() ; it ++ ){
int v = it->to ; if( v == f ) continue ;
dist[dep][v] = dist[dep][u] + it->len ;
dfs_getdis( v , u , dep ) ;
}
}
void dfs_Gr( int u , int f , int &G1 , int &G2 ){
siz[u] = 1 ;
for( vector ::iterator it = p1[u].begin() ; it != p1[u].end() ; it ++ ){
int v = it->to ; if( v == f || v == -1 ) continue ;
dfs_Gr( v , u , G1 , G2 ) ;
siz[u] += siz[v] ;
if( dif[G2] > dif[v] ) G1 = u , G2 = v ;
} dif[u] = abs( SIZ - 2 * siz[u] ) ;
}
int dfs_getTree( int u , int dep , int ssiz ){
if( ssiz == 1 ){ ::dep[u] = dep ; return u ; }
dfs_getdis( u , u , dep ) ;
int G1(0) , G2(0) , now = ++vcnt ;
SIZ = ssiz , dfs_Gr( u , u , G1 , G2 ) ;
for( vector ::iterator it = p1[G1].begin() ; it != p1[G1].end() ; it ++ )
if( it->to == G2 ){ val[now] = it->len , it->to = -1 ; break ; }
for( vector ::iterator it = p1[G2].begin() ; it != p1[G2].end() ; it ++ )
if( it->to == G1 ){ it->to = -1 ; break ; }
rs[now] = dfs_getTree( G1 , dep + 1 , ssiz - siz[G2] ) ;
ls[now] = dfs_getTree( G2 , dep + 1 , siz[G2] ) ;
fa[ ls[now] ] = fa[ rs[now] ] = now ;
return now ;
}
void preWork(){
vcnt = N , dfs_rebuild( 1 , 1 ) ;
dif[0] = 0x3f3f3f3f , dfs_getTree( 1 , 0 , vcnt ) ;
}
long long lv[23*370000] , rv[23*370000] , ans ;
int id_c , lp[23*370000] , rp[23*370000] , mp[23*370000] , root[370000] ;
int Merge( int x , int y , long long d ){
if( !x || !y ) return x + y ;
smax( ans , ( lv[x] + rv[y] + val[ mp[x] ] ) / 2 - d ) ;
smax( ans , ( rv[x] + lv[y] + val[ mp[x] ] ) / 2 - d ) ;
smax( lv[x] , lv[y] ) , lp[x] = Merge( lp[x] , lp[y] , d ) ;
smax( rv[x] , rv[y] ) , rp[x] = Merge( rp[x] , rp[y] , d ) ;
return x ;
}
int insert( int u ){
for( int i = dep[u] , x = u , las = 0 ; i > 0 ; i -- ){
mp[++id_c] = fa[u] ;
lv[id_c] = rv[id_c] = -1LL << 60 ;
if( u == ls[ fa[u] ] ) smax( lv[id_c] , dist[i][x] + toRoot[x] ) , lp[id_c] = las ;
if( u == rs[ fa[u] ] ) smax( rv[id_c] , dist[i][x] + toRoot[x] ) , rp[id_c] = las ;
las = id_c , u = fa[u] ;
} return id_c ;
}
void dfs_solve( int u , int f , long long d ){
root[u] = insert( u ) ; smax( ans , toRoot[u] - d ) ;
for( vector ::iterator it = p2[u].begin() ; it != p2[u].end() ; it ++ ){
int v = it->to ; if( v == f ) continue ;
dfs_solve( it->to , u , d + it->len ) ;
root[u] = Merge( root[u] , root[v] , d ) ;
}
}
void read_( int &x ){
x = 0 ; bool f = false ;
char ch = getchar() ;
while( ch < '0' || ch > '9' ){ if( ch == '-' ) f = true ; ch = getchar() ; }
while( ch >='0' && ch <='9' ) x = x * 10 + ch - '0' , ch = getchar() ;
x = f ? -x : x ;
}
int main(){
scanf( "%d" , &N ) ;
for( int i = 1 , u , v , w ; i < N ; i ++ ){
read_( u ) , read_( v ) , read_( w ) ;
In( u , v , w , p1 ) ;
} for( int i = 1 , u , v , w ; i < N ; i ++ ){
read_( u ) , read_( v ) , read_( w ) ;
In( u , v , w , p2 ) ;
} preWork() ; dfs_solve( 1 , 1 , 0 ) ;
printf( "%lld\n" , ans ) ;
}