Solution -「SP 6779」GSS7
阿新 • • 發佈:2020-07-27
\(\mathcal{Description}\)
給定一棵 \(n\) 個點的帶點權樹,\(q\) 次操作:
- 路徑點權賦值。
- 詢問路徑最大子段和(可以為空)。
\(n,q\le10^5\)。
\(\mathcal{Solution}\)
嘛……其實就是 GSS3 搬到樹上 qwq。應該可以熟練地列出轉移矩陣了叭,設 \(f(u)\) 為以 \(u\) 為端點的最大子段和,\(g(u)\) 為字首最大子段和,\(s_u\) 為 \(u\) 的重兒子(這題來練練樹剖 www),有:
\[\begin{bmatrix}g(u)\\f(u)\\0\end{bmatrix}=\begin{bmatrix}0&a_u&0\\-\infty&a_u&0\\-\infty&-\infty&0\end{bmatrix}\begin{bmatrix}g(s_u)\\f(s_u)\\0\end{bmatrix} \]
注意在樹剖跳重鏈求答案的時候,必須注意矩乘順序。例如對於路徑 \((u,v)\),欽定 \(u\) 為路徑起點,當 \(u\) 向上跳時,轉移矩陣按 DFN 降序;當 \(v\) 向上跳時轉移矩陣按 DFN 升序,所以線段樹應維護兩個乘法順序的矩陣積。
這道題有些卡常(而且我常數貌似很大 qwq),所以手玩一下轉移矩陣的冪,發現:
\[\begin{bmatrix}0&v&-\infty\\-\infty&v&0\\-\infty&-\infty&0\end{bmatrix}^k=\begin{bmatrix}0&\max\{v,kv\}&\max\{0,kv\}\\-\infty&kv&\max\{0,kv\}\\-\infty&-\infty&0\end{bmatrix} \]
就可以 \(\mathcal O(1)\) 求出矩陣冪了。
\(\mathcal{Code}\)
雖然長,但我覺得程式碼顏值挺高的 www。(大霧
#include <cstdio> # ifdef LOCAL_DEBUG const int MAXN = 5; # else const int MAXN = 1e5; # endif const int NINF = -( 1ll << 30 ); int n, m, ecnt, a[MAXN + 5], head[MAXN + 5]; int fa[MAXN + 5], dep[MAXN + 5], siz[MAXN + 5], son[MAXN + 5]; int dfc, dfn[MAXN + 5], ref[MAXN + 5], top[MAXN + 5]; inline int max_ ( const int a, const int 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 () { # ifdef LOCAL_DEBUG # define fgc getchar # endif 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 ( int 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]; inline void link ( const int s, const int t ) { graph[++ ecnt] = { t, head[s] }; head[s] = ecnt; } struct Matrix { int mat[3][3]; Matrix (): mat { NINF, NINF, NINF, NINF, NINF, NINF, NINF, NINF, NINF } {} inline int* operator [] ( const int key ) { return mat[key]; } inline Matrix operator * ( Matrix t ) { Matrix ret; for ( int i = 0; i < 3; ++ i ) { for ( int k = 0; k < 3; ++ k ) { for ( int j = 0; j < 3; ++ j ) { ret[i][j] = max_ ( ret[i][j], mat[i][k] + t[k][j] ); } } } return ret; } }; inline Matrix identity () { static Matrix I; I[0][0] = I[1][1] = I[2][2] = 0; return I; } inline Matrix make ( const int v ) { Matrix A; A[0][0] = A[0][2] = A[1][2] = A[2][2] = 0; A[0][1] = A[1][1] = v; return A; } inline Matrix power ( const int v, const int k ) { Matrix A; A[0][0] = A[2][2] = 0; A[0][1] = max_ ( v, k * v ); A[0][2] = A[1][2] = max_ ( 0, k * v ); A[1][1] = k * v; return A; } struct SegmentTree { Matrix S[2][MAXN * 2 + 5]; int tag[MAXN * 2 + 5]; inline int id ( const int l, const int r ) { return ( l + r ) | ( l != r ); } inline void pushup ( const int l, const int r ) { int rt = id ( l, r ), mid = l + r >> 1, lc = id ( l, mid ), rc = id ( mid + 1, r ); S[0][rt] = S[0][lc] * S[0][rc], S[1][rt] = S[1][rc] * S[1][lc]; } inline void pushas ( const int l, const int r, const int v ) { int rt = id ( l, r ); S[0][rt] = S[1][rt] = power ( v, r - l + 1 ), tag[rt] = v; } inline void pushdn ( const int l, const int r ) { int rt = id ( l, r ), mid = l + r >> 1; if ( tag[rt] == NINF ) return ; pushas ( l, mid, tag[rt] ), pushas ( mid + 1, r, tag[rt] ); tag[rt] = NINF; } inline void build ( const int l, const int r ) { int rt = id ( l, r ), mid = l + r >> 1; tag[rt] = NINF; if ( l == r ) return void ( S[0][rt] = S[1][rt] = make ( a[ref[l]] ) ); build ( l, mid ), build ( mid + 1, r ); pushup ( l, r ); } inline void assign ( const int l, const int r, const int al, const int ar, const int v ) { int mid = l + r >> 1; if ( al <= l && r <= ar ) return pushas ( l, r, v ); pushdn ( l, r ); if ( al <= mid ) assign ( l, mid, al, ar, v ); if ( mid < ar ) assign ( mid + 1, r, al, ar, v ); pushup ( l, r ); } inline Matrix query ( const int l, const int r, const int ql, const int qr, const bool type ) { int rt = id ( l, r ), mid = l + r >> 1; if ( ql <= l && r <= qr ) return S[type][rt]; pushdn ( l, r ); if ( qr <= mid ) return query ( l, mid, ql, qr, type ); else if ( mid < ql ) return query ( mid + 1, r, ql, qr, type ); else return ! type ? query ( l, mid, ql, qr, 0 ) * query ( mid + 1, r, ql, qr, 0 ): query ( mid + 1, r, ql, qr, 1 ) * query ( l, mid, ql, qr, 1 ); } } st; inline void DFS1 ( const int u, const int f ) { dep[u] = dep[fa[u] = f] + ( siz[u] = 1 ); for ( int i = head[u], v; i; i = graph[i].nxt ) { if ( ( v = graph[i].to ) ^ f ) { DFS1 ( v, u ), siz[u] += siz[v]; if ( siz[son[u]] < siz[v] ) son[u] = v; } } } inline void DFS2 ( const int u, const int tp ) { ref[dfn[u] = ++ dfc] = u, top[u] = tp; if ( son[u] ) DFS2 ( son[u], tp ); for ( int i = head[u], v; i; i = graph[i].nxt ) { if ( ( v = graph[i].to ) ^ fa[u] && v ^ son[u] ) { DFS2 ( v, v ); } } } inline void assignChain ( int u, int v, const int w ) { while ( top[u] ^ top[v] ) { if ( dep[top[u]] < dep[top[v]] ) u ^= v ^= u ^= v; st.assign ( 1, n, dfn[top[u]], dfn[u], w ); u = fa[top[u]]; } if ( dep[u] < dep[v] ) u ^= v ^= u ^= v; st.assign ( 1, n, dfn[v], dfn[u], w ); } inline int queryChain ( int u, int v ) { Matrix A ( identity () ), B ( identity () ); while ( top[u] ^ top[v] ) { if ( dep[top[u]] < dep[top[v]] ) { B = st.query ( 1, n, dfn[top[v]], dfn[v], 0 ) * B; v = fa[top[v]]; } else { A = A * st.query ( 1, n, dfn[top[u]], dfn[u], 1 ); u = fa[top[u]]; } } if ( dep[u] > dep[v] ) A = A * st.query ( 1, n, dfn[v], dfn[u], 1 ); else B = st.query ( 1, n, dfn[u], dfn[v], 0 ) * B; A = A * B; return max_ ( A[0][1], A[0][2] ); } 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 ); } DFS1 ( 1, 0 ), DFS2 ( 1, 1 ); st.build ( 1, n ); for ( int q = rint (), op, a, b, c; q --; ) { op = rint (), a = rint (), b = rint (); if ( op & 1 ) { wint ( queryChain ( a, b ) ); putchar ( '\n' ); } else { c = rint (); assignChain ( a, b, c ); } } return 0; }