Solution -「SP 6779」GSS7

\(\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;
}

猜你喜欢

转载自www.cnblogs.com/rainybunny/p/13384023.html