Loading

【笔记】动态 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;
}
posted @ 2022-06-18 15:19  7KByte  阅读(101)  评论(0编辑  收藏  举报