Solution -「BZOJ #3786」星系探索
阿新 • • 發佈:2021-07-08
\(\mathcal{Description}\)
Link.
給定一棵含 \(n\) 個點的有根樹,點有點權,支援 \(q\) 次操作:
- 詢問 \(u\) 到根的點權和;
- 修改 \(u\) 的父親,保證得到的圖仍是樹;
- 將 \(u\) 子樹內的點權增加一常數。
\(n\le10^5\),\(q\le3\times10^5\)。
\(\mathcal{Solution}\)
ETT (Euler Tour Tree),是一種能快速處理子樹移動的動態樹。本質上,它將樹儲存作尤拉序,由於子樹移動體現在尤拉序上是區間移動,那麼就能使用平衡樹維護序列。路徑查詢亦對應了一段區間內僅出現一次的結點貢獻和,記每個結點第一次出現為正貢獻,後一次出現為負貢獻就變成了區間查詢,亦能用平衡樹維護。本題即 ETT 板題。
複雜度自然是 \(\mathcal O((n+q)\log n)\) 的。
\(\mathcal{Code}\)
/*~Rainybunny~*/ #include <cstdio> #include <cassert> #include <cstdlib> #define rep( i, l, r ) for ( int i = l, rep##i = r; i <= rep##i; ++i ) #define per( i, r, l ) for ( int i = r, per##i = l; i >= per##i; --i ) typedef long long LL; const int MAXN = 1e5; int n, ecnt, head[MAXN + 5], w[MAXN + 5], root, st[MAXN + 5], ed[MAXN + 5]; struct Edge { int to, nxt; } graph[MAXN + 5]; inline void link( const int s, const int t ) { graph[++ecnt] = { t, head[s] }, head[s] = ecnt; } struct Treap { static const int MAXND = MAXN << 1; int node, ch[MAXND + 5][2], fa[MAXND + 5], siz[MAXND + 5], aux[MAXND + 5], sign[MAXND + 5], ssiz[MAXND + 5]; LL val[MAXND + 5], sum[MAXND + 5], tag[MAXND + 5]; Treap() { srand( 20120712 ); } inline int crtnd( const int v, const int s ) { int u = ++node; aux[u] = rand(), siz[u] = 1; sign[u] = ssiz[u] = s; val[u] = sum[u] = v * s; return u; } inline void pushad( const int u, const LL x ) { val[u] += sign[u] * x, tag[u] += x, sum[u] += x * ssiz[u]; } inline void pushup( const int u ) { siz[u] = siz[ch[u][0]] + siz[ch[u][1]] + 1; ssiz[u] = ssiz[ch[u][0]] + ssiz[ch[u][1]] + sign[u]; sum[u] = sum[ch[u][0]] + sum[ch[u][1]] + val[u]; } inline void pushdn( const int u ) { if ( tag[u] ) { if ( ch[u][0] ) pushad( ch[u][0], tag[u] ); if ( ch[u][1] ) pushad( ch[u][1], tag[u] ); tag[u] = 0; } } inline int merge( const int u, const int v ) { if ( !u || !v ) return u | v; pushdn( u ), pushdn( v ); if ( aux[u] < aux[v] ) { ch[u][1] = merge( ch[u][1], v ); if ( ch[u][1] ) fa[ch[u][1]] = u; return pushup( u ), u; } else { ch[v][0] = merge( u, ch[v][0] ); if ( ch[v][0] ) fa[ch[v][0]] = v; return pushup( v ), v; } } inline void rsplit( const int u, const int k, int& x, int& y ) { if ( !u ) return void( x = y = 0 ); fa[u] = 0, pushdn( u ); if ( siz[ch[u][0]] >= k ) { y = u, rsplit( ch[u][0], k, x, ch[y][0] ); if ( ch[y][0] ) fa[ch[y][0]] = y; } else { x = u, rsplit( ch[u][1], k - siz[ch[u][0]] - 1, ch[x][1], y ); if ( ch[x][1] ) fa[ch[x][1]] = x; } pushup( u ); } inline int rank( int u ) { int ret = siz[ch[u][0]] + 1; while ( fa[u] ) { if ( ch[fa[u]][1] == u ) ret += siz[ch[fa[u]][0]] + 1; u = fa[u]; } return ret; } } trp; inline void init( const int u ) { root = trp.merge( root, st[u] = trp.crtnd( w[u], 1 ) ); for ( int i = head[u]; i; i = graph[i].nxt ) init( graph[i].to ); root = trp.merge( root, ed[u] = trp.crtnd( w[u], -1 ) ); } inline LL query( const int u ) { int l = trp.rank( st[1] ), r = trp.rank( st[u] ), x, y, z; assert( l == 1 ); trp.rsplit( root, l - 1, x, y ), trp.rsplit( y, r - l + 1, y, z ); LL ret = trp.sum[y]; root = trp.merge( x, trp.merge( y, z ) ); return ret; } inline void change( const int u, const int p ) { int l = trp.rank( st[u] ), r = trp.rank( ed[u] ), tar = trp.rank( st[p] ), x, y, z, m; if ( tar < l ) { trp.rsplit( root, tar, x, y ); trp.rsplit( y, l - tar - 1, y, z ); trp.rsplit( z, r - l + 1, z, m ); } else { trp.rsplit( root, l - 1, x, y ); trp.rsplit( y, r - l + 1, y, z ); trp.rsplit( z, tar - r, z, m ); } root = trp.merge( trp.merge( x, z ), trp.merge( y, m ) ); } inline void update( const int u, const int p ) { int l = trp.rank( st[u] ), r = trp.rank( ed[u] ), x, y, z; trp.rsplit( root, l - 1, x, y ), trp.rsplit( y, r - l + 1, y, z ); trp.pushad( y, p ), root = trp.merge( x, trp.merge( y, z ) ); } int main() { scanf( "%d", &n ); for ( int i = 2, t; i <= n; ++i ) scanf( "%d", &t ), link( t, i ); rep ( i, 1, n ) scanf( "%d", &w[i] ); init( 1 ); char op[5]; int q, a, b; for ( scanf( "%d", &q ); q--; ) { scanf( "%s %d", op, &a ); if ( op[0] == 'Q' ) printf( "%lld\n", query( a ) ); else if ( op[0] == 'C' ) scanf( "%d", &b ), change( a, b ); else scanf( "%d", &b ), update( a, b ); } return 0; }