第一次學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