题目
点这里看题目。
分析
如果这道题可以换根,那它就是一道水题,可是换不得。
我们首先考虑 (p) 是根的时候应该怎么做。可以发现,对于所有情况总存在:
这里我们认为 ( ext{LCA}[l,r]) 为集合 ([l, r]) 的点的 ( ext{LCA}) ,括号决定区间的开或闭。
简单说明一下。记左边集合为 (A) ,右边为 (B) 。由于 ( ext{LCA}[l,r]) 是 (lsim r) 的点的 ( ext{LCA}) ,并且 (p) 就是根,所以 ( ext{LCA}[l,r]) 必然在 (p) 到 (lsim r) 的公共路径的任意一个点上,就应该有 (Bsubset A) 。又由于 ( ext{LCA}[l,r]) 是 (lsim r) 的最深的公共祖先,就有 (Asubset B) ,也就是 (A=B) 。
然后考虑 (p) 不是根的时候会怎么样。首先我们还是需要得到 (u= ext{LCA}[l,r]) 。然后考虑 (u) 和 (p) 的位置,也可以理解为 " 换根 " :
(mathcal{Case 1:}) (p) 是 (u) 的祖先。此时可以像上面一样直接判断。
(mathcal{Case 2:}) (p) 不是 (u) 的祖先,且 (p) 是 (lsim r) 的某些节点的祖先。此时如果将 (p) 提做根,那么 (lsim r) 必然会分散到 (p) 的多个子树中,答案为 (0) 。
(mathcal{Case 3:}) (p) 不是 (lsim r) 中任何点的祖先。如果将 (p) 提做根,此时 (lsim r) 的 ( ext{LCA}) 就不一定是 (u) 了。这里需要再分一些 (mathcal{Case}) 。
(mathcal{Case3.1:}) (u) 不是 (p) 作为根的情况下的 ( ext{LCA}) 。那么我们应该多考虑哪一个节点呢?显然这个节点(不妨称之为 (v) )应该是 (p) 的祖先。更近一步的,应该是 (v=max_{i=l}^r ext{LCA}{i,p}) (按照深度比较取大小)。对于 (w) 是 (v) 的祖先,当 (p) 换做根之后,(w) 的子树内必然不会包含 ([l,r]) 的所有点。
如果 (u) 不是 (p) 作为根的时候的 ( ext{LCA}) ,就说明 (p) 在 (u) 的子树内且 (v) 应该比 (u) 更深,正如下面这幅图:
(mathcal{Case3.2:}) (u) 是 (p) 作为根的情况下的 ( ext{LCA}) 。此时 (p) 就不应该在 (u) 的子树内,且 (v) 比 (u) 浅。如图:
这么分类讨论一下之后,问题就比较好解决了。我们只需要解决几个问题:
(mathcal{1.}) 查询连续几个点的 ( ext{LCA}) 。我们可以对点进行倍增, (lca[i,j]) 存储 ( ext{LCA}[i,i+2^j)) 。由于 ( ext{LCA}) 这个运算具有交换律、结合律和等幂性 ( 即 ( ext{LCA}{a,a}=a) ),我们可以花费单次 ( ext{LCA}) 的时间查询出连续几个点的 ( ext{LCA}) 。如果使用 ( ext{RMQ}) 查询 ( ext{LCA}) 可以做到 (O(nlog_2n)-O(1)) 解决。
(mathcal{2.}) 查询一个点是否是某些点的祖先,我们可以直接查询这个点内包含的标记点的数量。由于问题中标记点在一个区间,我们可以将树展开到 ( ext{DFS}) 序上,并利用可持久化线段树维护点的前缀上 DFS 序的情况,并直接查询。
(mathcal{3.}) 查询一个点到某些点的 ( ext{LCA}) 中最深的那一个。显然我们可以从起始点进行倍增,每次检查是否有标记点在下一步的点的子树中,如果没有我们就可以跳一下。最终我们可以得到最浅的一个子树内不包含任何标记点的点,它的父亲就是我们的目标。这个过程可以配合倍增 ( ext{LCA}) 理解。
时间复杂度是 (O(nlog_2^2n)) ,瓶颈是问题 (mathcal{3}) ,占用了两个 (log) 。
本题的可借鉴之处就在于它的分类讨论步骤和多次出现的倍增思想。树上换根问题,既可以 ( ext{LCT}) 维护,也可以像这样分类讨论。
代码
#include <cstdio>
const int MAXN = 2e5 + 5, MAXLOG = 20, MAXS = MAXN * MAXLOG;
template<typename _T>
void read( _T &x )
{
x = 0;char s = getchar();int f = 1;
while( s > '9' || s < '0' ){if( s == '-' ) f = -1; s = getchar();}
while( s >= '0' && s <= '9' ){x = ( x << 3 ) + ( x << 1 ) + ( s - '0' ), s = getchar();}
x *= f;
}
template<typename _T>
void write( _T x )
{
if( x < 0 ){ putchar( '-' ); x = ( ~ x ) + 1; }
if( 9 < x ){ write( x / 10 ); }
putchar( x % 10 + '0' );
}
struct edge
{
int to, nxt, w;
}Graph[MAXN << 1];
int s[MAXS], lch[MAXS], rch[MAXS];
int rt[MAXN], nsiz;
int f[MAXN][MAXLOG], lca[MAXN][MAXLOG];
int lg[MAXN];
int head[MAXN], dist[MAXN], dep[MAXN], DFN[MAXN], siz[MAXN];
int N, Q, cnt, ID, lg2;
void addEdge( const int from, const int to, const int W )
{
Graph[++ cnt].to = to, Graph[cnt].nxt = head[from], Graph[cnt].w = W;
head[from] = cnt;
}
void addE( const int from, const int to, const int W )
{
addEdge( from, to, W ), addEdge( to, from, W );
}
void DFS( const int u, const int fa )
{
f[u][0] = fa;
dep[u] = dep[fa] + 1, DFN[u] = ++ ID, siz[u] = 1;
for( int i = head[u], v ; i ; i = Graph[i].nxt )
if( ( v = Graph[i].to ) ^ fa )
dist[v] = dist[u] + Graph[i].w,
DFS( v, u ), siz[u] += siz[v];
}
void balance( int &u, const int stp )
{
for( int i = 0 ; ( 1 << i ) <= stp ; i ++ )
if( stp & ( 1 << i ) )
u = f[u][i];
}
int LCA( int u, int v )
{
if( dep[u] > dep[v] ) balance( u, dep[u] - dep[v] );
if( dep[v] > dep[u] ) balance( v, dep[v] - dep[u] );
if( u == v ) return u;
for( int i = lg2 ; ~ i ; i -- ) if( f[u][i] ^ f[v][i] ) u = f[u][i], v = f[v][i];
return f[u][0];
}
int rangeLCA( int l, int r )
{
int k = lg[r - l + 1];
return LCA( lca[l][k], lca[r - ( 1 << k ) + 1][k] );
}
void upt( const int x ) { s[x] = s[lch[x]] + s[rch[x]]; }
void copy( int a, int b ) { s[a] = s[b], lch[a] = lch[b], rch[a] = rch[b]; }
int update( const int x, const int l, const int r, const int p )
{
int cur = ++ nsiz; copy( cur, x );
if( l == r ) { s[cur] ++; return cur; }
int mid = l + r >> 1;
if( p <= mid ) lch[cur] = update( lch[x], l, mid, p );
else rch[cur] = update( rch[x], mid + 1, r, p );
upt( cur ); return cur;
}
int query( const int x, const int l, const int r, const int segL, const int segR )
{
if( ! x ) return 0;
if( segL <= l && r <= segR ) return s[x];
int mid = l + r >> 1, ret = 0;
if( segL <= mid ) ret += query( lch[x], l, mid, segL, segR );
if( mid < segR ) ret += query( rch[x], mid + 1, r, segL, segR );
return ret;
}
int query( const int u, const int L, const int R )
{
return query( rt[R], 1, N, DFN[u], DFN[u] + siz[u] - 1 )
- query( rt[L - 1], 1, N, DFN[u], DFN[u] + siz[u] - 1 );
}
void init()
{
DFS( 1, 0 );
lg[1] = 0;
for( int i = 2 ; i <= N ; i ++ )
lg[i] = lg[i - 1] + ( i == ( 1 << lg[i - 1] + 1 ) );
lg2 = lg[N];
for( int j = 1 ; j <= lg2 ; j ++ )
for( int i = 1 ; i <= N ; i ++ )
f[i][j] = f[f[i][j - 1]][j - 1];
for( int i = 1 ; i <= N ; i ++ ) lca[i][0] = i;
for( int j = 1 ; j <= lg2 ; j ++ )
for( int i = 1 ; i + ( 1 << j - 1 ) <= N ; i ++ )
lca[i][j] = LCA( lca[i][j - 1], lca[i + ( 1 << j - 1 )][j - 1] );
for( int i = 1 ; i <= N ; i ++ )
rt[i] = update( rt[i - 1], 1, N, DFN[i] );
}
int DCA( int u, const int L, const int R ) // Deepest Common Ancestor
{
int tmp;
for( int i = lg2 ; ~ i ; i -- )
if( tmp = f[u][i] )
if( ! query( tmp, L, R ) )
u = tmp;
return f[u][0];
}
int getDist( const int u, const int v )
{
return dist[u] + dist[v] - 2 * dist[LCA( u, v )];
}
int main()
{
read( N ), read( Q );
for( int i = 1, a, b, c ; i < N ; i ++ )
read( a ), read( b ), read( c ), addE( a, b, c );
init();
int P, L, R, lst = 0;
while( Q -- )
{
read( P ), read( L ), read( R );
P ^= lst, L ^= lst, R ^= lst;
int RLCA = rangeLCA( L, R ),
num = query( P, L, R );
if( num == R - L + 1 ) lst = getDist( P, RLCA );
else if( num ) lst = 0;
else
{
int t = DCA( P, L, R );
if( dep[t] > dep[RLCA] ) lst = getDist( P, t );
else lst = getDist( P, RLCA );
}
write( lst ), putchar( '
' );
}
return 0;
}