Solution -「洛谷 P6021」洪水
\(\mathcal{Description}\)
Link.
給定一棵 \(n\) 個點的帶點權樹,刪除 \(u\) 點的代價是該點點權 \(a_u\)。\(m\) 次操作:
- 修改單點點權。
- 詢問讓某棵子樹的根不可到達子樹內任意一片葉子的代價。
\(n,m\le2\times10^5\)。
\(\mathcal{Solution}\)
不考慮修改,列出 DP:
\[f(u)=\begin{cases}a_u&u\text{ is leaf}\\\min\{a_u,\sum_vf(v)\}&\text{otherwise}\end{cases} \]
單獨拿出實兒子 \(s_u\)
\[g(u)=\begin{cases}+\infty&u\text{ is leaf}\\\sum_{v\not=s_u}f(v)&\text{otherwise}\end{cases}\\\Rightarrow f(u)=\min\{a_u,f(s_u)+g(u)\} \]
定義矩乘的 \(+\) 為加法,\(\times\) 為取 \(\min\),有:
\[\begin{bmatrix}f(u)\\0\end{bmatrix}=\begin{bmatrix}g(u)&a_u\\+\infty&0\end{bmatrix}\begin{bmatrix}f(s_u)\\0\end{bmatrix} \]
用 LCT / 樹剖維護。若使用 LCT,詢問 \(u\) 子樹時,應 \(\operatorname{access}\) 原樹上 \(u\) 的父親,再 \(\operatorname{splay}\) \(u\),就能保證當前 \(u\) 的實鏈全部在子樹內,輸出 \(u\) 維護的矩乘答案即可。
還有一點,雖然 DP 是自下而上的,但把矩乘展開卻是由 \(u\) 向實兒子走的。注意乘法順序。
\(\mathcal{Code}\)
詢問的時候 \(\operatorname{access}\) 成了 LCT 上的父親調哭了 qwq。
#include <cstdio> #define calc( x ) ( min_ ( S[x][0][0], S[x][0][1] ) ) typedef long long LL; const int MAXN = 2e5; const LL INF = 1e14; int n, m, ecnt, a[MAXN + 5], head[MAXN + 5]; int srcfa[MAXN + 5], fa[MAXN + 5], ch[MAXN + 5][2]; inline LL min_ ( const LL a, const LL b ) { return a < b ? a : b; } 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, f = 1; char s = fgc (); for ( ; s < '0' || '9' < s; s = fgc () ) f = s == '-' ? -f : f; for ( ; '0' <= s && s <= '9'; s = fgc () ) x = x * 10 + ( s ^ '0' ); return x * f; } inline void wint ( LL x ) { if ( x < 0 ) putchar ( '-' ), x = -x; if ( 9 < x ) wint ( x / 10 ); putchar ( x % 10 ^ '0' ); } struct Edge { int to, nxt; } graph[MAXN * 2 + 5]; struct Matrix { LL mat[2][2]; Matrix (): mat { INF, INF, INF, INF } {} inline LL* operator [] ( const int key ) { return mat[key]; } inline Matrix operator * ( Matrix& t ) { Matrix ret; for ( int i = 0; i < 2; ++ i ) { for ( int k = 0; k < 2; ++ k ) { for ( int j = 0; j < 2; ++ j ) { ret[i][j] = min_ ( ret[i][j], mat[i][k] + t[k][j] ); } } } return ret; } } G[MAXN + 5], S[MAXN + 5]; inline void link ( const int s, const int t ) { graph[++ ecnt] = { t, head[s] }; head[s] = ecnt; } inline bool nroot ( const int x ) { return ch[fa[x]][0] == x || ch[fa[x]][1] == x; } inline void pushup ( const int x ) { S[x] = G[x]; if ( ch[x][0] ) S[x] = S[ch[x][0]] * S[x]; if ( ch[x][1] ) S[x] = S[x] * S[ch[x][1]]; } inline void rotate ( const int x ) { int y = fa[x], z = fa[y], k = ch[y][1] == x; fa[x] = z; if ( nroot ( y ) ) ch[z][ch[z][1] == y] = x; ch[y][k] = ch[x][k ^ 1]; if ( ch[x][k ^ 1] ) fa[ch[x][k ^ 1]] = y; pushup ( ch[fa[y] = x][k ^ 1] = y ), pushup ( x ); } inline void splay ( const int x ) { for ( int y; nroot ( x ); rotate ( x ) ) { if ( nroot ( y = fa[x] ) ) { rotate ( x ^ y ^ ch[y][0] ^ ch[fa[y]][0] ? x : y ); } } pushup ( x ); } inline void access ( int x ) { for ( int y = 0; x; x = fa[y = x] ) { splay ( x ); if ( ch[x][1] ) G[x][0][0] += calc ( ch[x][1] ); if ( y ) G[x][0][0] -= calc ( y ); ch[x][1] = y, pushup ( x ); } } inline LL initDP ( const int u, const int fath ) { LL sum = INF; fa[u] = srcfa[u] = fath; for ( int i = head[u], v; i; i = graph[i].nxt ) { if ( ( v = graph[i].to ) ^ fath ) { if ( sum == INF ) sum = 0; sum += initDP ( v, u ); } } G[u][0][0] = sum, G[u][0][1] = a[u], G[u][1][1] = 0; return S[u] = G[u], min_ ( sum, a[u] ); } int main () { n = rint (); for ( int i = 1; i <= n; ++ i ) a[i] = rint (); for ( int i = 1, u, v; i < n; ++ i ) { u = rint (), v = rint (); link ( u, v ), link ( v, u ); } initDP ( 1, 0 ); m = rint (); for ( int x, t, op; m --; ) { for ( op = fgc (); op < 'A' || 'Z' < op; op = fgc () ); x = rint (); if ( op == 'Q' ) { if ( srcfa[x] ) access ( srcfa[x] ); splay ( x ); wint ( calc ( x ) ), putchar ( '\n' ); } else { t = rint (); access ( x ), splay ( x ); G[x][0][1] += t, pushup ( x ); } } return 0; }