CF838B
一道不错的题。
首先观察这棵树,得到以下事实:
- \(1\) 为树根。
- 树上的边是由父亲连向儿子的有向边。
- 除 \(1\) 以外的节点都有一条连向 \(1\) 的有向边。
- 边权为正。
可知任意两点间都存在最短路,且这条最短路是不难求的。假设要求从 \(u\) 到 \(v\) 的最短路,设 \(dis1_u\) 为 \(1\) 到 \(u\) 的路径长度,\(dis2_u\) 为 \(u\) 到 \(1\) 的路径长度,分以下两种情况讨论:
- \(u\) 是 \(v\) 的祖先
显然,此时的最短路即为 \(u\) 到 \(v\) 的路径长度。由于边权为正,因此多走其它的边一定不优。答案即为 \(dis1_v - dis1_u\)。
- \(u\) 不是 \(v\) 的祖先
由于树上的边都是由深度小的点连向深度大的点,因此 \(u\) 无法只通过树上的边到达 \(v\),需要先回到 \(1\) 再去 \(v\)。而 \(u\) 能通过树上的边到达的点即为以 \(u\) 为根的子树。设 \(z\) 为以 \(u\) 为根的子树内一点,我们需要最小化 \(u\) 到 \(z\) 的距离加上 \(z\) 到 \(1\) 的距离的和,用式子表示为 \(\min{dis1_z-dis1_u+dis2_z}\)。\(dis1_u\) 在最后处理即可,于是变为 \(\min{dis1_z+dis2_z}\),转化成了子树求最小值的问题,使用 dfs 序和线段树解决即可。
接下来讨论如何处理修改操作。对于树上的边,我们观察发现会导致以边的终点为根的子树的 \(dis1\) 发生改变,直接修改增量即可。如果不是树上的边,对该边的起点做单点修改即可。
注意单点查询 \(dis1\) 时,由于维护的是 \(dis1+dis2\),因此需要减去对应的 \(dis2\)(当然用两棵线段树分开维护也可以)。
时间复杂度 \(O(m\log n)\)。
#include <iostream>
#include <cstdio>
#define int long long
#define N 300001
#define MAXN 30000000000000
using namespace std;
int n,T,Lx[N],Rx[N],dis1[N],dis2[N],hd[N],tot,cnt,fa[N][19],dep[N],rev[N];
struct E
{
int u,v,w,nxt;
}e[N * 2];
struct T
{
int lzy[N * 4],mi[N * 4],v[N * 4];
void pushup( int u )
{
mi[u] = min( mi[u << 1] , mi[u << 1 | 1] );
return;
}
void pushdown( int u )
{
if( lzy[u] )
{
mi[u << 1] += lzy[u];
lzy[u << 1] += lzy[u];
mi[u << 1 | 1] += lzy[u];
lzy[u << 1 | 1] += lzy[u];
lzy[u] = 0;
}
return;
}
void build( int u , int l , int r )
{
if( l == r )
{
v[u] = mi[u] = dis1[rev[l]] + dis2[rev[l]];
return;
}
int mid = ( l + r ) >> 1;
build( u << 1 , l , mid );
build( u << 1 | 1 , mid + 1 , r );
pushup( u );
}
void update( int u , int l , int r , int L , int R , int x )
{
if( L <= l && r <= R )
{
mi[u] += x;
lzy[u] += x;
return;
}
pushdown( u );
int mid = ( l + r ) >> 1;
if( L <= mid ) update( u << 1 , l , mid , L , R , x );
if( R > mid ) update( u << 1 | 1 , mid + 1 , r , L , R , x );
pushup( u );
return;
}
int query1( int u , int l , int r , int L , int R )
{
if( L <= l && r <= R ) return mi[u];
pushdown( u );
int mid = ( l + r ) >> 1,res = MAXN;
if( L <= mid ) res = min( res , query1( u << 1 , l , mid , L , R ) );
if( R > mid ) res = min( res , query1( u << 1 | 1 , mid + 1 , r , L , R ) );
return res;
}
int query2( int u , int l , int r , int x )
{
if( l == r ) return mi[u] - dis2[rev[l]];
pushdown( u );
int mid = ( l + r ) >> 1;
if( x <= mid ) return query2( u << 1 , l , mid , x );
else return query2( u << 1 | 1 , mid + 1 , r , x );
}
}t;
void add( int u , int v , int w )
{
tot ++;
e[tot].u = u;
e[tot].v = v;
e[tot].w = w;
e[tot].nxt = hd[u];
hd[u] = tot;
}
void dfs1( int u , int ff )
{
dep[u] = dep[ff] + 1;
Lx[u] = ++ cnt;
rev[cnt] = u;
for( int i = hd[u] ; i ; i = e[i].nxt )
{
int v = e[i].v;
if( Lx[v] ) continue;
dis1[v] = dis1[u] + e[i].w;
dfs1( v , u );
}
Rx[u] = cnt;
}
signed main()
{
ios::sync_with_stdio( false );
cin.tie( 0 );
cout.tie( 0 );
int op,u,v,w;
cin >> n >> T;
for( int i = 1 ; i < n ; i ++ )
{
cin >> u >> v >> w;
add( u , v , w );
}
dfs1( 1 , 0 );
for( int i = 1 ; i < n ; i ++ )
{
cin >> u >> v >> w;
add( u , v , w );
dis2[u] = w;
}
t.build( 1 , 1 , n );
while( T -- )
{
cin >> op >> u >> v;
if( op == 1 )
{
if( u < n ) t.update( 1 , 1 , n , Lx[e[u].v] , Rx[e[u].v] , v - e[u].w );
else t.update( 1 , 1 , n , Lx[e[u].u] , Lx[e[u].u] , v - e[u].w ),dis2[e[u].u] = v;
e[u].w = v;
}
if( op == 2 )
{
if( Lx[u] <= Lx[v] && Rx[v] <= Rx[u] ) cout << t.query2( 1 , 1 , n , Lx[v] ) - t.query2( 1 , 1 , n , Lx[u] ) << '\n';
else cout << t.query1( 1 , 1 , n , Lx[u] , Rx[u] ) - t.query2( 1 , 1 , n , Lx[u] ) + t.query2( 1 , 1 , n , Lx[v] ) << '\n';
}
}
return 0;
}

浙公网安备 33010602011771号