【笔记】动态 DP
用于解决一类支持修改的 DP 问题。
用矩阵维护 DP 的转移,然后用 DS 维护转移矩阵,就可以支持在线修改。
例题:【模板】"动态 DP" & 动态树分治
给定一棵 \(n\) 个点的树,点带点权。
有 \(m\) 次操作,每次操作给定 \(x,y\),表示修改点 \(x\) 的权值为 \(y\)。
你需要在每次操作之后求出这棵树的最大权独立集的权值大小。
重链剖分,然后将重儿子的贡献和轻儿子的贡献分开算。
我们记录 \(g_{x,0/1}\) 表示 \(x\) 和它所有轻儿子子树的答案,\(f_{x,0/1}\) 表示 \(x\) 子树的答案。\(0/1\) 表示选与不选。
那么我们修改一个点的点权,相当于修改一个点的 \(g\),把 \(g\) 写成矩阵 \(\begin{bmatrix}g_{x,0}&g_{x,1}\\g_{x,0}&-\inf\end{bmatrix}\),那么 \(f\) 就是重链 \(g\) 矩阵的乘积。树链剖分后用 线段树维护即可。
科技:全局平衡二叉树
我们都知道树剖线段树是两个 \(\log\),而使用 splay 的 LCT 却是一个 \(\log\) 的。原因是树剖的复杂度是链数乘 DS,而 LCT 的复杂度可以均摊到全局 splay 上。
但是对于静态树问题直接上 LCT 既难写又很慢。那么我们可以根据树的形态直接构造出最优形态的平衡二叉树,然后按 LCT 的方式维护即可。比树剖还好写。
#define N 1000005
int n, m, u[N];
int fa[N], top[N], sz[N], ls[N], son[N], f[N][2];
vector<int>e[N];
struct mat{int a[2][2];}g[N], b[N];
mat operator*(mat x, mat y){
mat 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;
};
void dfs(int x,int ff){
fa[x] = ff, sz[x] = 1, f[x][1] = u[x];
go(y, e[x])if(y != ff){
dfs(y, x), sz[x] += sz[y];
f[x][0] += max(f[y][0], f[y][1]),
f[x][1] += f[y][0];
if(sz[y] > sz[son[x]])son[x] = y;
}
}
void dfs2(int x,int tp){
ls[x] = 1, top[x] = tp;
if(!son[x])return;
dfs2(son[x], tp);
go(y, e[x])if(y != son[x] && y != fa[x])dfs2(y, y), ls[x] += sz[y];
}
int c[N], t, p[N], pa[N], ll[N], rr[N];
void upd(int x){
if(ll[x] && rr[x])b[x] = b[ll[x]] * g[x] * b[rr[x]];
else if(ll[x])b[x] = b[ll[x]] * g[x];
else if(rr[x])b[x] = g[x] * b[rr[x]];
else b[x] = g[x];
}
int calc(int l,int r,int s){
if(l > r)return 0;
int sum = 0;
rep(i, l, r){
sum += ls[c[i]];
if(sum * 2 >= s){
p[ll[c[i]] = calc(l, i - 1, sum - ls[c[i]])] = c[i];
p[rr[c[i]] = calc(i + 1, r, s - sum)] = c[i];
upd(c[i]); return c[i];
}
}assert(false); return ~0;
}
void ins(int x){
if(top[x] != x || !fa[x])return;
int l = max(f[x][0], f[x][1]), r = f[x][0], y = fa[x];
g[y].a[0][0] += l, g[y].a[1][0] += l, g[y].a[0][1] += r;
}
inline void maintain(int x){
while(true){
while(true){
if(pa[x])break;
upd(x), x = p[x];
}
int pl = max(b[x].a[0][0], b[x].a[1][0]);
int pr = max(b[x].a[0][1], b[x].a[1][1]);
swap(pl, pr), cmx(pl, pr);
upd(x); if(-1 == pa[x])break;
int ql = max(b[x].a[0][0], b[x].a[1][0]);
int qr = max(b[x].a[0][1], b[x].a[1][1]);
swap(ql, qr), cmx(ql, qr);
int y = pa[x];
g[y].a[0][0] += ql - pl, g[y].a[1][0] += ql - pl,
g[y].a[0][1] += qr - pr; x = y;
}
}
int lst = 0;
int main() {
read(n, m);
rp(i, n)read(u[i]);
rp(i, n)g[i].a[0][1] = u[i], g[i].a[1][1] = inf_;
rp(i, n - 1){
int x, y; read(x, y);
e[x].pb(y), e[y].pb(x);
}dfs(1, 0), dfs2(1, 1);
rp(i, n)ins(i);
fa[1] = ~0;
rp(i, n)if(!son[i]){
t = 0; int x = i;
while(true){
c[++t] = x;
if(x == top[x])break;
x = fa[x];
}
pa[calc(1, t, sz[x])] = fa[x];
}
int rt = 0;
rp(i, n)if(-1 == pa[i])rt = i;
while(m--){
int x, y; read(x, y), x ^= lst;
g[x].a[0][1] += y - u[x], u[x] = y;
maintain(x);
printf("%d\n", lst = max(max(b[rt].a[0][0], b[rt].a[0][1]), max(b[rt].a[1][0], b[rt].a[1][1])));
}
return 0;
}