[HNOI2016]树

题目

  点这里看题目。

分析

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

LCA 的查询

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

深度的查询

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

  时间复杂度:也就那么\(O(\text{“*()#U*(!@&U(%&”的巨大常数}\times n\log_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( '\n' );
	}
	return 0;
}
posted @ 2020-06-01 19:22  crashed  阅读(264)  评论(2编辑  收藏  举报