zoukankan      html  css  js  c++  java
  • 『MdOI R1』Treequery

    题目

    点这里看题目。

    分析

    如果这道题可以换根,那它就是一道水题,可是换不得。

    我们首先考虑 (p) 是根的时候应该怎么做。可以发现,对于所有情况总存在:

    [igcap_{i=l}^rE(p,i)=E(p, ext{LCA}[l,r]) ]

    这里我们认为 ( 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;
    }
    
  • 相关阅读:
    学习Faster R-CNN代码roi_pooling(二)
    应用安全
    应用安全
    应用安全
    应用安全
    应用安全
    红队
    应用安全
    应用安全
    应用安全
  • 原文地址:https://www.cnblogs.com/crashed/p/13455561.html
Copyright © 2011-2022 走看看