動態dp複習筆記

EternalEpic發表於2024-10-02

第一次學ddp的時候是2019年學長帶我們覆盤noip2018的保衛王國那道題,當時自然是不大會的,也不是很有就業空間,這幾天CF有道正解是dsu的序列dp題被哥哥用ddp艹了過去,所以現在得複習一下ddp。

我們先從經典的樹剖做法引入(全域性平衡二叉樹做法在我更新LCT複習筆記後再補)

ddp最經典的例子就是待修改求樹的最大點獨立集。如果是個靜態問題我們可以用dp輕鬆解決,大致就是 \(f_{u,0}\) 表示這個點不取的最大值,\(f_{u,1}\) 是取。然後轉移有

\[f_{u,0} = \sum \max(f_{v,0}, f_{v,1}) \]

\[f_{u,1} = a_u + \sum f_{v,0} \]

考慮待修改,顯然的可以考慮樹剖。我們知道求斐波那契數列可以用矩陣快速冪。其實dp方程的轉移也可以用矩陣乘法,只不過是魔改版: $ c_{i,j} = \max_{1 \leq k \leq n}(a_{i,k} + b_{k, j})$。 那我們可以把剛才的dp式子改寫,並把轉移變成連乘積,從而每條重鏈都維護這麼一個連乘積。這樣我在修改某個點的點權時只要修改它到根的路徑上的乘積即可,也就是跳top[u]一直跳到跟,中間用線段樹維護單點修改區間查詢。時間複雜度 \(O(nlog^{2}n)\)

P4719 【模板】"動態 DP"&動態樹分治

const int N = 1e5 + 5;
int n, m, a[N];
vector <int> G[N];

const int mod = 1e9 + 7;
inline void inc(int &x, int y) { x = x + y >= mod ? x + y - mod : x + y; }
inline int qpow(int a, int b = mod - 2) {
	int ret = 1;
	for (; b; b >>= 1, a = 1ll * a * a % mod)
		if (b & 1) ret = 1ll * ret * a % mod;
	return ret;
}

const int M = 2;
struct Matrix {
	int g[M][M];
	Matrix(void) { Ms(g, -0x3F); } inline void clear(void) { *this = Matrix(); }
	inline void construct(void) { Ms(g, 0); for (int i = 1; i <= n; i++) g[i][i] = 1; }
	inline int* operator [] (const int idx) { return g[idx]; }
	inline void Swap(int x, int y) { for (int i = 1; i <= n; i++) swap(g[x][i], g[y][i]); }
	inline void Mul(int x, int k) { for (int i = 1; i <= n; i++) g[x][i] = 1ll * g[x][i] * k % mod; }
	inline void Modify(int x, int y, int k) { for (int i = 1; i <= n; i++) inc(g[x][i], 1ll * g[y][i] * k % mod); }
	inline Matrix operator * (const Matrix&rhs) {
		Matrix ret;
		for (int i = 0; i < M; i++)
		for (int j = 0; j < M; j++)
		for (int k = 0; k < M; k++) chkmax(ret.g[i][j], g[i][k] + rhs.g[k][j]);
		return ret;
	}
} val[N];

int dep[N], fat[N], dfn[N], seg[N], top[N], sze[N], son[N], lst[N], timer;
inline void DfsFir(int u) {
	sze[u] = 1;
	for (auto v : G[u]) {
		if (v == fat[u]) continue;
		fat[v] = u; dep[v] = dep[u] + 1;
		DfsFir(v); sze[u] += sze[v];
		if (sze[son[u]] < sze[v]) son[u] = v;
	}
}

int f[N][2];
inline void DfsSec(int u, int t) {
	top[u] = t; dfn[u] = ++timer;
	seg[timer] = u; chkmax(lst[t], timer);
	f[u][0] = 0, f[u][1] = a[u];
	val[u].g[0][0] = val[u].g[0][1] = 0;
	val[u].g[1][0] = a[u];
	if (son[u]) {
		DfsSec(son[u], t);
		f[u][0] += max(f[son[u]][0], f[son[u]][1]);
		f[u][1] += f[son[u]][0];
	}
	for (auto v : G[u]) {
		if (v == fat[u] || v == son[u]) continue;
		DfsSec(v, v);
		f[u][0] += max(f[v][0], f[v][1]);
		f[u][1] += f[v][0];
		val[u].g[0][0] += max(f[v][0], f[v][1]);
		val[u].g[0][1] += max(f[v][0], f[v][1]);
		val[u].g[1][0] += f[v][0];
	}
}


struct SegmentTree {
	Matrix t[N << 2];
	inline void pushup(int pos) { t[pos] = t[pos << 1] * t[pos << 1 | 1]; }
	inline void build(int pos, int l, int r) {
		if (l == r) { t[pos] = val[seg[l]]; return; }
		int mid = l + r >> 1;
		build(pos << 1, l, mid);
		build(pos << 1 | 1, mid + 1, r);
		pushup(pos);
	}
	
	inline void update(int pos, int l, int r, int x) {
		if (l == r) { t[pos] = val[seg[l]]; return; }
		int mid = l + r >> 1;
		if (x <= mid) update(pos << 1, l, mid, x);
		else update(pos << 1 | 1, mid + 1, r, x);
		pushup(pos);
	}
	
	inline Matrix query(int pos, int l, int r, int L, int R) {
		if (L <= l && R >= r) return t[pos];
		int mid = l + r >> 1; Matrix ret;
		if (L <= mid) {
			ret = query(pos << 1, l, mid, L, R);
			if (R > mid) ret = ret * query(pos << 1 | 1, mid + 1, r, L, R);
		} else if (R > mid) ret = query(pos << 1 | 1, mid + 1, r, L, R);
		return ret;
	}
} T;

inline void Modify(int x, int v) {
	val[x].g[1][0] += v - a[x]; a[x] = v;
	Matrix bef, aft;
	while (x) {
		bef = T.query(1, 1, n, dfn[top[x]], lst[top[x]]);
		T.update(1, 1, n, dfn[x]);
		aft = T.query(1, 1, n, dfn[top[x]], lst[top[x]]);
		x = fat[top[x]];
		
		val[x].g[0][0] += max(aft.g[0][0], aft.g[1][0]) - max(bef.g[0][0], bef.g[1][0]);
		val[x].g[0][1] = val[x].g[0][0];
		val[x].g[1][0] += aft.g[0][0] - bef.g[0][0];
	}
}

signed main(void) {
	read(n), read(m);
	for (int i = 1; i <= n; i++) read(a[i]);
	for (int i = 1, u, v; i < n; i++) {
		read(u), read(v);
		G[u].push_back(v);
		G[v].push_back(u);
	}
	
	DfsFir(1); DfsSec(1, 1); T.build(1, 1, n); 
	
	while (m--) {
		int x, y;
		read(x), read(y);
		Modify(x, y);
		Matrix ret = T.query(1, 1, n, 1, lst[1]);
		writeln(max(ret.g[1][0], ret.g[0][0]));
	}
	//fwrite(pf, 1, o1 - pf, stdout);
	return 0;
}

推薦例題還有:
SPOJ GSS3 - Can you answer these queries III
NOIP2018 保衛王國
SDOI2017 切樹遊戲
CFR752(div2) F

相關文章