??点这里看题目。
??一个真正的树套树的题目。
??大体思路非常简单,就是把从模板树上面复制下来的子树用一个点来代表,再插入到大树里面。接着就“正常”地维护一下倍增和深度,查询也跟“正常”的一样,先查 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;
}
原文:https://www.cnblogs.com/crashed/p/13026977.html