动态DP(ddp)
动态dp(ddp)
昨天模拟赛 T3 考了到 ddp 板子题,没认真学习的我丢掉了 60 分……
在这里就是讲讲 模板题。
题面描述
求一个树的最大独立集,点带权,权值带修改。$n,q \leq 10^5$
题解
首先考虑没有带修改,或者是 $O(nq)$ 的做法。
就是直接令 $f_{u,0/1}$ 表示考虑 $u$ 这个子树,对于 $u$ 这个节点选择/不选的最大独立集,则$$ f_{u,0} = \sum_{v} \max(f_{v,0},f_{v,1})\\ f_{u,1} = a_u + \sum_{v} f_{v,0}\\ $$ 考虑修改,发现一个点修改后只会对祖先这一部分的 dp 值进行修改。
由于下标只有 2 维,非常容易想到矩阵优化这个 dp 的转移。
而细想发现,直接将儿子的转移矩阵乘起来并不是对的。这个矩阵优化只能对一个序列有效。
但是,一个序列对应树上的什么形态?链!链让我们想到什么?重链剖分!
对于每个点,考虑如何直接的写出从 $f_{son_u}$ 转移到 $f_u$ 的转移矩阵呢?
考虑到影响矩阵的只有轻儿子的部分,即:
令 $g_{u,0/1}$ 表示考虑 $u$ 这个子树,不考虑重儿子的情况下,选/不选的最大独立集,考虑从 $g$ 转移到 $f$,则$$ 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} $$ 这样就可以写出转移矩阵了,注意这里的矩阵乘法是广义矩阵乘法,即$$ \begin{pmatrix} g_{u,0}&g_{u, 0}\\g_{u,1}&-inf\end{pmatrix} \times \begin{pmatrix}f_{son_u,0}&f_{son_u,1}\end{pmatrix} = \begin{pmatrix}f_{u,0}&f_{u,1}\end{pmatrix} $$ 这样,我们就可以在 $g_u$ 已知的情况下,就可以用 树链剖分+线段树维护矩阵 快速的求出 $f$ 啦!那我们考虑如何求解 $g$,
其实可以发现每次修改,$g$ 的值只会在我们跳轻边的父节点会修改,那么我们直接暴力更改 $g$ 的值即可。
复杂度 $o(2^3n\log^2n)$,可以通过。
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
const int inf = 1e9 + 1;
struct matrix {
int a[2][2];
matrix (int aa = 0, int bb = 0, int cc = 0, int dd = 0) {
a[0][0] = aa; a[0][1] = bb; a[1][0] = cc; a[1][1] = dd;
}
void print () {
for (int i = 0; i < 2; ++i) {
for (int j = 0; j < 2; ++j) printf ("%d ", a[i][j]); printf ("\n");
}
}
} p[N], I;
matrix operator * (const matrix x, const matrix y) {
matrix z;
z.a[0][0] = max (x.a[0][0] + y.a[0][0], x.a[0][1] + y.a[1][0]);
z.a[0][1] = max (x.a[0][0] + y.a[0][1], x.a[0][1] + y.a[1][1]);
z.a[1][0] = max (x.a[1][0] + y.a[0][0], x.a[1][1] + y.a[1][0]);
z.a[1][1] = max (x.a[1][0] + y.a[0][1], x.a[1][1] + y.a[1][1]);
return z;
}
struct Node {
int l, r; matrix x;
};
struct segment_tree {
Node t[N << 2];
void pushup (int x) {
t[x].x = t[x << 1].x * t[x << 1 | 1].x;
}
void build (int x, int l, int r) {
t[x].l = l; t[x].r = r;
if (l == r) { t[x].x = p[l]; return ; }
int mid = (l + r) >> 1;
build (x << 1, l, mid); build (x << 1 | 1, mid + 1, r);
pushup(x);
}
void update (int x, int k, matrix p) {
// cout << x <<endl;
if (t[x].l == t[x].r) {
t[x].x = p; return ;
} int mid = (t[x].l + t[x].r) >> 1;
if (k <= mid) update (x << 1, k, p);
else update (x << 1 | 1, k, p);
pushup (x);
}
matrix query (int x, int l, int r) {
// cout << x << endl;
// cout << t[x].l <<" "<< t[x].r << endl;
// cout << t[x].l << " " << t[x].r << endl; t[x].x.print();
if (l <= t[x].l && t[x].r <= r) return t[x].x;
int mid = (t[x].l + t[x].r) >> 1; matrix res = I;
if (l <= mid) res = res * query (x << 1, l, r);
if (r > mid) res = res * query (x << 1 | 1, l, r);
return res;
}
} T;
struct node {
int to, nxt;
} edge[N << 1];
int cnt, head[N];
int a[N], f[N][2], g[N][2];
int tim, dfn[N], low[N];
int F[N], sz[N], tp[N], son[N];
void add (int u, int v) {
cnt ++;
edge[cnt].to = v;
edge[cnt].nxt = head[u];
head[u] = cnt;
}
void dfs1 (int u, int fa) {
sz[u] = 1; F[u] = fa;
for (int i = head[u]; i; i = edge[i].nxt) {
int v = edge[i].to;
if (v == fa) continue; dfs1(v,u);
if (sz[son[u]] < sz[v]) son[u] = v; sz[u] += sz[v];
}
}
void dfs2 (int u, int fa, int top) {
dfn[u] = ++tim; tp[u] = top; low[top] = tim;
if (son[u]) dfs2 (son[u], u, top); g[u][1] += a[u];
for (int i = head[u]; i; i = edge[i].nxt) {
int v = edge[i].to;
if (v == fa || v == son[u]) continue;
dfs2 (v, u, v); g[u][0] += max (f[v][0], f[v][1]); g[u][1] += f[v][0];
} p[dfn[u]] = matrix(g[u][0], g[u][0], g[u][1], -inf);
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];
// cout << u << " " << f[u][0] << " " << f[u][1] <<endl;
// cout << u << " " << f[u][0] << " " << f[u][1] << endl;
// cout << u << endl;; p[u].print() ; cout << endl;
}
void update (int u, int v) {
int s0 = 0, s1 = v - a[u]; a[u] = v;
while (u >= 1) {
// cout << u << " " << s0 << " " << s1 << endl;
// cout << dfn[tp[u]] << " " << low[tp[u]] << endl;
// cout << F[tp[u]] << endl;
g[u][0] += s0; g[u][1] += s1;
p[dfn[u]] = matrix(g[u][0], g[u][0], g[u][1], -inf);
matrix lst = T.query (1, dfn[tp[u]], low[tp[u]]);
T.update (1, dfn[u], p[dfn[u]]);
matrix use = T.query (1, dfn[tp[u]], low[tp[u]]);
// cout << u << " "<<g[u][0] << " " << g[u][1] << endl;
s0 = max (use.a[0][0], use.a[1][0]) - max (lst.a[0][0], lst.a[1][0]);
s1 = use.a[0][0] - lst.a[0][0]; u = F[tp[u]];
// cout << use.a[0][0] << " " << use.a[1][0] << endl;
// cout << lst.a[0][0] << " " << lst.a[1][0] << endl;
}
}
int main() {
I = matrix (0, -inf, -inf, 0);
int n, m; scanf ("%d%d", &n, &m);
for (int i = 1; i <= n; ++i) scanf ("%d", &a[i]);
for (int i = 1; i < n; ++i) {
int u, v; scanf ("%d%d", &u, &v); add (u, v); add (v, u);
} dfs1 (1, 0); dfs2 (1, 0, 1); T.build (1, 1, n);
// for (int i = 1; i <= n; ++i) printf ("%d ", son[i]); printf ("\n");
// for (int i = 1; i <= n; ++i) printf ("%d %d\n", dfn[i], low[i]);
for (int Q = 1; Q <= m; ++Q) {
int x, t; scanf ("%d%d",&x,&t); update (x, t);
matrix ans = T.query (1, dfn[1], low[1]);
printf ("%d\n", max (ans.a[0][0], ans.a[1][0]));
}
return 0;
}
浙公网安备 33010602011771号