zoukankan      html  css  js  c++  java
  • [HNOI2016]树

    题目

      点这里看题目。

    分析

      一个真正的树套树的题目。
      大体思路非常简单,就是把从模板树上面复制下来的子树用一个点来代表,再插入到大树里面。接着就“正常”地维护一下倍增和深度,查询也跟“正常”的一样,先查 LCA ,再用深度做差。这种思路......形象地称为树套树。
      什么,你说这是水题?开什么玩笑,我可调了一个下午......
      具体来说,本体有亿些细节:

    LCA 的查询

      查询,我们先找到两个点会在大树上哪个点相遇,然后再把大树上的点提出来,还原成小树上的形态,再进行一次 LCA ,再把这个 LCA 还原到大树的编号上。
      1.大树上的 LCA :直接对点维护倍增。注意,由于我们会把形态还原,因此我们需要知道,原来两个点具体会跳到树上哪两个点;这个可以通过记录大树上的点的真实父亲得到。
      2.还原成小树:我们需要知道大树上的编号具体对应小树上的哪些点。我们可以对每个“虚点”记录它所管辖的编号区间。因此大树上的点就可以通过二分找到自己所属的“虚点”,并且知道自己在大树中的大小排名。接着我们需要将它和小树上的点对应起来,这可以通过把排名丢到主席树里面查第 K 大实现。
      3.小树上的 LCA :同上,它可以和大树共用一个倍增数组。
      4.还原到大树:同上,我们需要知道它在子树中的大小排名,这可以继续到主席树里面查询,然后就可以转到大树上来。

    深度的查询

      1.大树上的“虚点”深度:直接动态维护。
      2.大树上的真实深度:我们对每个“虚点”,维护它的“假根”到树根的深度。这个可以在插入的时候,将父亲所在的“虚点”的深度查出来,再加上父亲到父亲的“假根”的真实深度,计算得出。查询就查所在“虚点”的真实深度加上到“假根”的深度的和。

      时间复杂度:也就那么(O( ext{“*()#U*(!@&U(%&”的巨大常数} imes nlog_2n))吧。

    代码

    #include <cmath>
    #include <cstdio>
    
    typedef long long LL;
    
    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' );
    }
    
    template<typename _T>
    _T ABS( const _T a )
    {
    	return a < 0 ? -a : a;
    }
    
    struct edge
    {
    	int to, nxt;
    }Graph[MAXN << 1];
    
    LL fa[MAXN], dep[MAXN], rdep[MAXN], nsiz[MAXN];
    int s[MAXS], lch[MAXS], rch[MAXS];
    int f[MAXN][MAXLOG], rot[MAXN];
    int head[MAXN], tsiz[MAXN], DFN[MAXN], seq[MAXN], ref[MAXN];
    LL N, M, Q, tot, siz, ID, lg2, cnt;
    
    void upt( const int u ) { s[u] = s[lch[u]] + s[rch[u]]; }
    void copy( int a, int b ) { s[a] = s[b], lch[a] = lch[b], rch[a] = rch[b]; }
    
    int update( const int u, const int l, const int r, const int pos )
    {
    	int cur = ++ siz, mid = l + r >> 1; copy( cur, u );
    	if( l == r ) { s[cur] ++; return cur; }
    	if( pos <= mid ) lch[cur] = update( lch[u], l, mid, pos );
    	else rch[cur] = update( rch[u], mid + 1, r, pos );
    	upt( cur );
    	return cur;
    }
    
    int query( const int u, const int l, const int r, const int segL, const int segR )
    {
    	if( ! u ) return 0;
    	if( segL <= l && r <= segR ) return s[u];
    	int mid = l + r >> 1, ret = 0;
    	if( segL <= mid ) ret += query( lch[u], l, mid, segL, segR );
    	if( segR > mid ) ret += query( rch[u], mid + 1, r, segL, segR );
    	return ret;
    }
    
    int Kth( const int u, const int v, const int l, const int r, const int rnk )
    {
    	if( l == r ) return l;
    	int mid = l + r >> 1, num = s[lch[v]] - s[lch[u]];
    	if( rnk <= num ) return Kth( lch[u], lch[v], l, mid, rnk );
    	return Kth( rch[u], rch[v], mid + 1, r, rnk - num );
    }
    
    void addEdge( const int from, const int to )
    {
    	Graph[++ cnt].to = to, Graph[cnt].nxt = head[from];
    	head[from] = cnt;
    }
    
    void maintain( const int u )
    {
    	for( int j = 1 ; j <= lg2 ; j ++ )
    		f[u][j] = f[f[u][j - 1]][j - 1];
    }
    
    void DFS( const int u, const int fath )
    {
    	nsiz[u] = ref[u] = u;
    	DFN[u] = ++ ID, seq[ID] = u;
    	fa[u] = f[u][0] = fath, maintain( u );
    	rdep[u] = dep[u] = dep[fath] + 1, tsiz[u] = 1;
    	for( int i = head[u], v ; i ; i = Graph[i].nxt )
    		if( ( v = Graph[i].to ) ^ fath )
    			DFS( v, u ), tsiz[u] += tsiz[v];
    }
    
    int bel( const LL id )
    {
    	int l = 1, r = tot, mid;
    	while( r - l > 1 )
    	{
    		if( id <= nsiz[mid = l + r >> 1] ) r = mid;
    		else l = mid + 1;
    	}
    	if( id <= nsiz[l] ) return l;
    	return r;
    }
    
    void balance( LL &u, const int step )
    {
    	for( int i = 0 ; ( 1 << i ) <= step ; i ++ )
    		if( step & ( 1 << i ) )
    			u = f[u][i];
    }
    
    LL getRef( const LL u )
    {
    	if( u <= N ) return u;
    	LL com = bel( u ), fr = ref[com];
    	return Kth( rot[DFN[fr] - 1], rot[DFN[fr] + tsiz[fr] - 1], 1, N, u - nsiz[com - 1] );
    }
    
    LL getDep( const LL u )
    {
    	LL com = bel( u ), tru = getRef( u );
    	return rdep[com] + rdep[tru] - rdep[ref[com]];
    }
    
    LL LCA( LL u, LL 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];
    }
    
    LL getDist( LL from, LL to )
    {
    	LL u = bel( from ), v = bel( to ), ff, tt;
    	if( u == v ) ff = from, tt = to;
    	else
    	{
    		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 ) 
    		{
    			u = bel( from ), v = bel( to );
    			if( dep[u] > dep[v] ) balance( u, dep[u] - dep[v] - 1 ), ff = fa[u], tt = to;
    			if( dep[v] > dep[u] ) balance( v, dep[v] - dep[u] - 1 ), ff = from, tt = fa[v];
    		}
    		else 
    		{
    			for( int i = lg2 ; ~ i ; i -- ) if( f[u][i] ^ f[v][i] ) u = f[u][i], v = f[v][i];
    			ff = fa[u], tt = fa[v];
    		}
    	}
    	LL lca = LCA( getRef( ff ), getRef( tt ) ), in = bel( ff );
    	lca = in <= N ? lca : 
    		  query( rot[DFN[ref[in]] + tsiz[ref[in]] - 1], 1, N, 1, lca ) 
    		- query( rot[DFN[ref[in]] - 1], 1, N, 1, lca ) 
    		+ nsiz[in - 1];
    	return getDep( from ) + getDep( to ) - 2 * getDep( lca );
    }
    
    signed main()
    {
    	read( N ), read( M ), read( Q );
    	for( int i = 1, a, b ; i < N ; i ++ ) read( a ), read( b ), addEdge( a, b ), addEdge( b, a );
    	lg2 = log2( N ), DFS( 1, 0 ), tot = N;
    	for( int i = 1 ; i <= N ; i ++ ) rot[i] = update( rot[i - 1], 1, N, seq[i] );
    	LL fr, to, a, b;
    	for( int i = 1 ; i <= M ; i ++ )
    	{
    		read( fr ), read( to );
    		ref[++ tot] = fr, fa[tot] = to;
    		nsiz[tot] = nsiz[tot - 1] + tsiz[fr];
    		int com = bel( to );
    		rdep[tot] = rdep[com] + rdep[getRef( to )] - rdep[ref[com]] + 1;
    		dep[tot] = dep[com] + 1;
    		f[tot][0] = com, maintain( tot );
    	}
    	for( int i = 1 ; i <= Q ; i ++ )
    	{
    		read( a ), read( b );
    		write( getDist( a, b ) ), putchar( '
    ' );
    	}
    	return 0;
    }
    
  • 相关阅读:
    centos mongo数据库搭建
    闪屏页白屏或者显示旧图
    在Sqlserver下巧用行列转换日期的数据统计
    读 《.Net 之美》解析.Net Remoting (应用程序域)-- Part.1
    MVC的自定义动作过滤器(一)
    【算法】快排
    【编程范式】C语言1
    排序
    日志记录类
    邮箱发送类
  • 原文地址:https://www.cnblogs.com/crashed/p/13026977.html
Copyright © 2011-2022 走看看