动态dp 学习笔记
基本概念
我们知道 dp 一般只能解决一个确定的问题。
如果有多次询问,每次修改一个权值,我们就需要这个 ddp 了。
例题
求树的最大带权独立集。每次修改一个点的权值并再次询问。\(n,m \leq 10^5\)。
广义矩阵乘法
定义一个新的矩阵乘法:
\[C_{i,j} = \max\limits_{1 \leq k \leq n} A_{i,k} + B_{k,j}
\]
这玩意儿满足结合律。
转移方程
令 \(f_{i,0}\) 表示不选 \(i\) 的最大答案,\(f{{i,1}\) 表示选择 \(i\) 的最大答案。
再定义一个 \(g_{i,0}\) 和 \(g_{i,1}\),和 \(f\) 的区别就是这个 \(g\) 忽略了其重儿子(即只考虑轻儿子)。
然后就有一个转移方程:
\[f_{u,0}=\sum\max(f_{v,0},f_{v,1})
\]
\[f_{u,1}=val_u+\sum f_{v,0}
\]
\(g\) 的转移就不写了,不过写成矩乘形式是这样的:
\[\begin{bmatrix}g_{u,0}&g_{u,0}\\g_{u,1}&-\infty\end{bmatrix} \times \begin{bmatrix}f_{s,0}\\f_{s,1}\end{bmatrix}=\begin{bmatrix}f_{u,0}\\f_{u,1}\end{bmatrix}
\]
这个挺显然。注意是用的广义矩乘。\(s\) 代表 \(u\) 的重儿子。
维护
- 查询
注意到最终答案就是 \(1\) 到其所在重链的底部的 \(\begin{bmatrix}g_{u,0}&g_{u,0}\\g_{u,1}&-\infty\end{bmatrix}\) 矩乘和。
考虑维护链上的矩乘和,可以用重链剖分+线段树维护。
- 修改
假设修改点 \(x\),那么只需要修改 \(x\) 本身对应的矩阵、以及其 \(\text{fa[top[x]]}...\) 的对应矩阵。体现到图上,就是:

其中橙色点是修改点,绿色点是矩阵受到影响的点(蓝色是重链,注意图中剖分方式有误,为了方便理解)。
之所以只有这些会受影响,是因为 \(g\) 只和轻儿子有关。修改的时候算一下偏移值就好了。
时间复杂度 \(O(n \log^2 n)\)。
代码
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#define Maxn 100005
using namespace std;
const int INF = 1e8;
inline int read() {
int x = 0, f = 1;
char c = getchar();
while(c < '0' || c > '9') {
if(c == '-') f = -1;
c = getchar();
}
while('0' <= c && c <= '9') {
x = x * 10 + c - '0';
c = getchar();
}
return x * f;
}
int n, m, a[Maxn];
/* Matrix Part */
struct Mat {
int v[2][2];
void init() {memset(v, -63, sizeof(v));}
void giv(int a, int b, int c, int d) {
init(); v[0][0] = a; v[0][1] = b; v[1][0] = c; v[1][1] = d;
}
}
G[Maxn], temp;
Mat Mul(Mat a, Mat b) {
Mat c; c.init();
for(int i = 0; i < 2; ++i)
for(int j = 0; j < 2; ++j)
for(int k = 0; k < 2; ++k)
c.v[i][j] = max(c.v[i][j], a.v[i][k] + b.v[k][j]);
return c;
}
/* Edge Part */
struct Edge {
int next, to;
}
edge[Maxn * 2];
int head[Maxn], edge_num;
void add_edge(int from, int to) {
edge[++edge_num].next = head[from];
edge[edge_num].to = to;
head[from] = edge_num;
}
/* Dividing Part */
int depth[Maxn], fa[Maxn], siz[Maxn], son[Maxn];
inline void dfs1(int u) {
siz[u] = 1;
for(int i = head[u]; i; i = edge[i].next) {
int v = edge[i].to;
if(v == fa[u]) continue;
depth[v] = depth[u] + 1; fa[v] = u;
dfs1(v);
siz[u] += siz[v];
if(siz[son[u]] < siz[v]) son[u] = v;
}
}
int dfn[Maxn], top[Maxn], end[Maxn], f[Maxn][2], g[Maxn][2], cnt;
inline int dfs2(int u, int nowtop) {
dfn[u] = ++cnt; top[u] = nowtop;
if(!son[u]) {
f[u][1] = g[u][1] = a[u];
G[dfn[u]].giv(0, 0, g[u][1], -INF);
return end[u] = u;
}
g[u][1] = a[u];
end[u] = dfs2(son[u], nowtop);
for(int i = head[u]; i; i = edge[i].next) {
int v = edge[i].to;
if(v == fa[u] || v == son[u]) continue;
dfs2(v, v);
g[u][0] += max(f[v][0], f[v][1]);
g[u][1] += f[v][0];
}
f[u][0] = g[u][0] + max(f[son[u]][0], f[son[u]][1]);
f[u][1] = g[u][1] + f[son[u]][0];
G[dfn[u]].giv(g[u][0], g[u][0], g[u][1], -INF);
return end[u];
}
/* Segment_tree Part */
#define lid id << 1
#define rid id << 1 | 1
struct Tree {
int l, r; Mat mul;
}
tree[Maxn * 4];
struct Seg {
inline void build(int id, int l, int r) {
tree[id].l = l, tree[id].r = r;
if(l == r) {
tree[id].mul = G[l];
return;
}
int mid = (l + r) >> 1;
build(lid, l, mid); build(rid, mid + 1, r);
tree[id].mul = Mul(tree[lid].mul, tree[rid].mul);
}
inline void modify(int id, int pos) {
if(tree[id].l == tree[id].r) {
tree[id].mul = temp; return;
}
int mid = (tree[id].l + tree[id].r) >> 1;
if(pos <= mid) modify(lid, pos);
else modify(rid, pos);
tree[id].mul = Mul(tree[lid].mul, tree[rid].mul);
}
inline Mat query(int id, int l, int r) {
// cout << l << " " << r << " " << tree[id].l << " " << tree[id].r << endl; system("pause");
if(tree[id].l == l && tree[id].r == r) return tree[id].mul;
int mid = (tree[id].l + tree[id].r) >> 1;
if(r <= mid) return query(lid, l, r);
else if(mid < l) return query(rid, l, r);
else return Mul(query(lid, l, mid), query(rid, mid + 1, r));
}
}
seg;
/* Operation Part */
void update(int x, int y) {
g[x][1] += (y - a[x]); a[x] = y;
while(x) {
temp.giv(g[x][0], g[x][0], g[x][1], -INF);
Mat bef = seg.query(1, dfn[top[x]], dfn[end[x]]);
seg.modify(1, dfn[x]);
Mat aft = seg.query(1, dfn[top[x]], dfn[end[x]]);
x = fa[top[x]];
g[x][0] += max(aft.v[0][0], aft.v[1][0]) - max(bef.v[0][0], bef.v[1][0]);
g[x][1] += aft.v[0][0] - bef.v[0][0];
}
}
int main() {
n = read(); m = read();
for(int i = 1; i <= n; ++i) a[i] = read();
int u, v;
for(int i = 1; i < n; ++i) {
u = read(); v = read();
add_edge(u, v); add_edge(v, u);
}
depth[1] = 1;
dfs1(1); dfs2(1, 1);
seg.build(1, 1, n);
for(int i = 1; i <= m; ++i) {
u = read(); v = read();
update(u, v);
Mat ans = seg.query(1, dfn[1], dfn[end[1]]);
printf("%d\n", max(ans.v[0][0], ans.v[1][0]));
}
return 0;
}

浙公网安备 33010602011771号