【关于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;
}