Link-Cut Tree 学习小记

【关于LCT的标记】

1. 在查询的时候传递标记与Splay时按从祖到下地传递标记是等价的。

2. 在将一个点上旋之前一定要把身上的标记传递出去。

3. 挂上标记没有必要立即生效。

4. 切忌从虚父亲处获得标记。(不是同一条重链)

5. 注意像乘0这样的标记。

【关于改变树形态和Splay】

1. 改之前要确保标记都传完了。

2. Cut过程要把虚父亲删除,Link过程构成轻链后没必要update。

3. 改完以后一定要确保都Splay或Update了。(保证数据同步性)

【关于数据上传过程】

1. 如果标记不是马上生效的,在Update时要算上标记。

2. 结构改变一定要记得Update。

3. 没有必要专门开一个Cnt 域来处理类似区间加这样的标记,传完标记后update一下就好了。


【实现细节】

1. 虚父亲没必要专门开一个域来记录,只需要判定其父亲是否有一个儿子为它本身即可判断是否为虚父亲,可以写一个短的函数 OnTop( x ) 来记录。

2. Splay结点x到当前链顶,停止条件应该是OnTop( x )。

3. 若其父亲的父亲为根,进行双旋的话有可能会让这个祖先降两层。

4. Splay过程只需判断父亲为链顶,或同向则可以判定为单旋还是双旋,若要避免3中的情况只需要判爷爷是否为链顶即可。

5. Cut, Query之类的过程如无必要不要判定谁上谁下,Evert一下就可以简单地实现了。

6. 封装得好看起来比较不违和,不封装看起来比较短。


代码:

#include <fstream>
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int N = 200005;

#define fo( i, x, y ) for ( int i=x; i<y; ++i )

ifstream in( "tree.in" );
ofstream out( "tree.out" );

int v[N];
int n, Q, PCNT;

struct LCT
{
	struct Splay
	{
		int Fa, s[2];
		bool Rev;
	} g[N];

	inline bool OnTop( int x )
	{
		int F = g[x].Fa;
		return !( g[F].s[0]==x || g[F].s[1]==x );
	}
	inline void push( int x )
	{
		++PCNT;
		if ( !g[x].Rev || !x ) return;
		fo ( i, 0, 2 ) g[ g[x].s[i] ].Rev ^= 1;
		swap( g[x].s[0], g[x].s[1] ), g[x].Rev = 0;
	}
	inline void Rotate( int x )
	{
		int y = g[x].Fa, z = g[y].Fa;
		bool p = ( g[y].s[1]==x ), q = p ^ 1;
		int A = g[x].s[q];
		g[y].Fa = x, g[y].s[p] = A, g[x].Fa = z;
		g[x].s[q] = y, g[A].Fa = y;
		if ( g[z].s[0]==y ) g[z].s[0] = x;
		else if ( g[z].s[1]==y ) g[z].s[1] = x;
	}
	void Splay( int x )
	{
		push( x );
		for ( int y=g[x].Fa, z=g[y].Fa; !OnTop( x ); y=g[x].Fa, z=g[y].Fa )
		{
			push( z ), push( y ), push( x );
			if ( OnTop( y ) || OnTop( z ) || g[z].s[0]==y ^ g[y].s[0]==x ) Rotate( x );
			else Rotate( y ), Rotate( x );
		}
	}
	inline void Access( int x )
	{
		int Pre = 0;
		do
		{
			Splay( x );
			g[x].s[0] = Pre;
			Pre = x, x = g[x].Fa;
		} while ( x );
	}
	inline void Evert( int x )
	{
		Access( x );
		Splay( x );
		g[x].Rev ^= 1;
	}
	inline void Link( int x, int y )
	{
		Evert( y );
		g[y].Fa = x;
	}
	inline void Cut( int x, int y )
	{
		Evert( x ), Access( y ), Splay( y );
		g[y].s[1] = g[ g[y].s[1] ].Fa = 0;
	}
	inline bool query( int x, int y )
	{
		if ( x==y ) return 1;
		Evert( x ), Access( y ); 
		Splay( x ), Splay( y );
		return g[y].s[1]==x;
	}
} f;

void preprocessing()
{
	ios :: sync_with_stdio( 0 );
	in >> n >> Q;
	fo ( i, 1, n + 1 ) in >> v[i];
	int x, y;
	fo ( i, 1, n )
	{
		in >> x >> y;
		f.Link( x, y );
	}
}

void solve()
{
	int Type, x, y, LasAns = 0;
	fo ( Case, 0, Q )
	{
		in >> Type >> x >> y;
		x ^= LasAns, y ^= LasAns;
		if ( Type==1 ) f.Cut( x, y );
		else if ( Type==2 ) 
		{
			if ( f.query( x, y ) ) LasAns = v[x] * v[y];
			else LasAns = v[x] + v[y];
			out << LasAns << endl;
		}
		else v[x] = y;
	}
	out << PCNT << endl;
}

int main()
{
	preprocessing();
	solve();
	in.close(); out.close();
	return 0;
}


你可能感兴趣的:(tree,dynamic,学习笔记)