Solution -「CF 855G」Harry Vs Voldemort
阿新 • • 發佈:2020-08-05
\(\mathcal{Description}\)
Link.
給定一棵 \(n\) 個點的樹和 \(q\) 次加邊操作。求出每次操作後,滿足 \(u,v,w\) 互不相等,路徑 \((u,w)\) 與 \((v,w)\) 無重複邊的有序三元組 \((u,v,w)\) 的個數。
\(n,q\le10^5\)。
\(\mathcal{Solution}\)
考慮原樹上,以某個點為 \(w\) 的貢獻。記 \(\operatorname{contr}(u)\) 為 \(u\) 的貢獻,則有:
\[\operatorname{contr}(u)=(n-1)(n-1)-\sum_{v\in son_u}siz_v^2-(n-siz_u)(n-siz_u) \]
又發現一個邊雙中的每個點都應是等價的。所以對於以 \(u\) 為頂點的邊雙,維護 \(val_u=\sum_{v\in son_u}siz_v^2\) 和大小 \(s_u\),我們也能求出它的貢獻:
\[\operatorname{contr}(u)=s_u((n-s_u)^2-val_u-(n-siz_u)^2)+2s_u(s_u-1)(n-s_u)+s_u(s_u-1)(s_u-2) \]
加邊時,暴力爬樹,並用並查集維護連通邊雙即可。
複雜度 \(\mathcal O(n\log n)\)(並查集不帶啟發式合併)。
\(\mathcal{Code}\)
#include <cstdio> #include <assert.h> typedef long long LL; const int MAXN = 1e5; int n, ecnt, head[MAXN + 5]; int fa[MAXN + 5], dep[MAXN + 5], siz[MAXN + 5], blk[MAXN + 5]; LL ans, val[MAXN + 5]; struct Edge { int to, nxt; } graph[MAXN * 2 + 5]; inline void link ( const int s, const int t ) { graph[++ ecnt] = { t, head[s] }; head[s] = ecnt; } inline char fgc () { static char buf[1 << 17], *p = buf, *q = buf; return p == q && ( q = buf + fread ( p = buf, 1, 1 << 17, stdin ), p == q ) ? EOF : *p ++; } inline int rint () { int x = 0; char d = fgc (); for ( ; d < '0' || '9' < d; d = fgc () ); for ( ; '0' <= d && d <= '9'; d = fgc () ) x = x * 10 + ( d ^ '0' ); return x; } inline void wint ( const LL x ) { if ( 9 < x ) wint ( x / 10 ); putchar ( x % 10 ^ '0' ); } struct DSU { int fa[MAXN + 5]; inline void init () { for ( int i = 1; i <= n; ++ i ) fa[i] = i; } inline int find ( const int x ) { return x ^ fa[x] ? fa[x] = find ( fa[x] ) : x; } inline bool unite ( int x, int y ) { x = find ( x ), y = find ( y ); return x ^ y ? fa[x] = y, true : false; } } dsu; inline void init ( const int u ) { siz[u] = blk[u] = 1; for ( int i = head[u], v; i; i = graph[i].nxt ) { if ( ( v = graph[i].to ) ^ fa[u] ) { dep[v] = dep[fa[v] = u] + 1, init ( v ); siz[u] += siz[v]; val[u] += 1ll * siz[v] * siz[v]; } } } inline void calc ( const int u, const int k ) { assert ( u == dsu.fa[u] ); int s = blk[u]; ans += 1ll * k * s * ( 1ll * ( n - s ) * ( n - s ) - val[u] - 1ll * ( n - siz[u] ) * ( n - siz[u] ) ); ans += 2ll * k * s * ( s - 1 ) * ( n - s ); ans += 1ll * k * s * ( s - 1 ) * ( s - 2 ); } inline void merge ( const int u, const int v ) { assert ( u == dsu.fa[u] && v == dsu.fa[v] && dep[u] < dep[v] ); calc ( u, -1 ), calc ( v, -1 ); val[u] -= 1ll * siz[v] * siz[v], val[u] += val[v], blk[u] += blk[v]; calc ( u, 1 ), dsu.unite ( v, u ); } int main () { n = rint (), dsu.init (); for ( int i = 1, u, v; i < n; ++ i ) { u = rint (), v = rint (); link ( u, v ), link ( v, u ); } init ( 1 ); for ( int i = 1; i <= n; ++ i ) calc ( i, 1 ); wint ( ans ), putchar ( '\n' ); for ( int q = rint (), u, v; q --; ) { u = rint (), v = rint (); while ( dsu.find ( u ) ^ dsu.find ( v ) ) { if ( dep[dsu.find ( u )] < dep[dsu.find ( v )] ) u ^= v ^= u ^= v; u = dsu.find ( u ); merge ( dsu.find ( fa[u] ), u ); } wint ( ans ), putchar ( '\n' ); } return 0; }