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