动态dp总结

学习中……

模板题

题目传送门
题解传送门
首先考虑不修改的情况,dp方程是显然的。
\(dp_{i,0/1}\) 表示强制当前点选或者不选的最大收益。
显然 $$dp_{i,0}=\sum_{j\in V}\max(dp_{j,0},dp_{j,1})$$

\[dp_{i,1}=\sum_{j\in V} dp_{j,0} \]

考虑设计 g 数组表示当前点除了不考虑重儿子的dp值,设 \(j\) 为点 \(i\) 的重儿子,则可以得到dp转移方程 $$dp_{i,0}=g_{i,0}+max(dp_{j,0},dp_{j,1})$$

\[dp_{i,1}=g_{i,1}+dp_{j,0} \]

然后我们想到,动态dp是通过矩阵维护的,所以,考虑设计矩阵。
但是,和数组上的动态dp不同,树上的是从后往前转移的,所以设计状态时要反着搞。
然后就设计出来了转移的矩阵,非常愉快。
接下来我们加入修改,考虑一次修改会对什么情况造成结果,显然是对于每一个当前修改的点是当这个点位于其轻儿子的子树中时,会对其转移矩阵造成修改。
换句话说,对于当前点位于的重链上,并没有转移矩阵会被修改。
所以,我们可以每次沿着重链往上跳,最多跳log条重链,也就是说,时间复杂度变为 \(O(n\log^2(n))\)
可以通过本题。

需要注意的一点是:最开始建树的时候,不是根据点的编号建的,而是根据点在树上dfn序来建的,主包就是因为这个卡了一下午。
代码如下:

#include<bits/stdc++.h>
#define push_back emplace_back
#define int long long 
using namespace std;
int n,q,tot=0;
const int N = 1e5 + 100,mod = 998244353,inf = 1e18;
int p[N],a[N],siz[N],fa[N],bl[N],g[N][2],f[N][2],len[N],dfn[N],dd[N];
vector<int>e[N];
struct node {
	int a[3][3];	
	inline node() {
		for(int i = 1;i <= 2;i++) {
			for(int j = 1;j <= 2;j++) {
			 	a[i][j] = -inf;
			}
		}
	}
	friend node operator *(node a,node b) {
		node ans;
		for(int i = 1;i <= 2;i++) {
			for(int j = 1;j <= 2;j++) {
				for(int k = 1;k <= 2;k++) {
					ans.a[i][j] = max(ans.a[i][j],a.a[i][k] + b.a[k][j]);
				}
			}
		}
		return ans;
	}
}t[N*4];

inline int read(){
	int x;cin>>x;return x;
}

void write(int x) {
	if(x < 0) {putchar('-');x=-x;}
	if(x > 9) write(x / 10);
	putchar((char) (x % 10+'0'));
}

void dfs(int x) {
	siz[x] = 1;
	for(auto y:e[x]) {
		if(y == fa[x]) continue;
		fa[y] = x;
		dfs( y);
		siz[x] = siz[y] + siz[x];
	}
	// cerr<<x<<" "<<siz[x]<<endl; 
} 

void dfs1(int x,int chain) {
	g[x][1]+=a[x];f[x][1]=a[x];
	// if(e[x].size()==1&&x!=1) f[x][1]+=a[x];
	dfn[x] = ++tot;
	bl[x]=chain;
	len[chain]++;
	int k = 0;
	for(auto y:e[x]) {
		if(y == fa[x]) continue;
		if(!k || siz[k] < siz[y]) k=y;
	}
	if(!k) {
		f[x][1]=a[x];
		return ;
	}
	dfs1(k,chain);
	f[x][0] += max(f[k][0],f[k][1]);
	// f[x][0]%=mod;
	f[x][1] += f[k][0];
	// f[x][1]%=mod;
	for(auto y:e[x]) {
		if(y == fa[x]) continue;
		if(y == k) continue;
		dfs1(y,y);
		f[x][0]+=max(f[y][0],f[y][1]);
		f[x][1]+=f[y][0];
		g[x][1] += f[y][0];
		g[x][0] += max(f[y][0],f[y][1]);
		// g[x][0]%=mod;g[x][1]%=mod;
	}
}

void build(int p,int l,int r) {
	if(l==r) {
		t[p].a[1][1] = g[dd[l]][0];
		t[p].a[1][2] = g[dd[l]][0];
		t[p].a[2][1] = g[dd[l]][1];
		t[p].a[2][2] = -inf;
		// cout<<p<<" "<<t[p].a[1][1]<<" "<<t[p].a[1][2]<<" "<<t[p].a[2][1]<<" "<<t[p].a[2][2]<<endl;
		return ;
	}
	int mid = (l + r) >> 1;
	build(p * 2,l,mid);
	build(p * 2 + 1,mid + 1,r);
	t[p] = t[p * 2] * t[p * 2 + 1];
	// cerr<<p<<" "<<l<<" "<<mid<<" "<<r<<" "<<t[p].a[1][1]<<" "<<t[p].a[1][2]<<endl;
}

void updata(int p,int l,int r,int tl) {
	if(l==r) {
		t[p].a[1][1] = g[dd[l]][0];
		t[p].a[1][2] = g[dd[l]][0];
		t[p].a[2][1] = g[dd[l]][1];
		t[p].a[2][2] = -inf;
		return ;
	}
	int mid = (l + r) >> 1;
	if(tl <= mid) updata(p * 2,l,mid,tl);
	else updata(p * 2 + 1,mid + 1,r,tl);	
	t[p] = t[p * 2] * t[p * 2 + 1];	
}

node ask(int p,int l,int r,int tl,int tr) {
	if(tl <= l && r <= tr) {
		return t[p];
	}
	int mid = (l + r) >> 1;
	node a;
	for(int i = 1; i <= 2; i++){
		for(int j = 1; j <= 2; j++){
			a.a[i][j] = (i == j) ? (0): (-inf);
		}
	}
	if(tl <= mid) a = a * ask(p * 2,l,mid,tl,tr);
	if(mid < tr) a = a * ask(p * 2 + 1,mid + 1,r,tl,tr);
	return a;
}

void change(int p,int v) {
	int now = p;
	g[p][1] -= a[p];g[p][1] += v;
	a[p] = v;
	node x,y;
	while(now != 0) {
		x = ask(1,1,n,dfn[bl[now]],dfn[bl[now]] + len[bl[now]] - 1);
		updata(1,1,n,dfn[now]);
		y = ask(1,1,n,dfn[bl[now]],dfn[bl[now]] + len[bl[now]] - 1);
		int u = fa[bl[now]];
		// cerr<<x.a[1][1]<<" "<<x.a[1][2]<<" "<<y.a[1][1]<<" "<<y.a[1][2]<<endl;
		g[u][0] -= max(x.a[1][1],x.a[2][1]);g[u][0] += max(y.a[1][1],y.a[2][1]);
		g[u][1] -= x.a[1][1]; g[u][1] += y.a[1][1];
		now=u;
	}
}

signed main() {
	n=read();q=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();
		e[u].push_back(v);
		e[v].push_back(u);
	}
	dfs(1);
	dfs1(1,1);
	for(int i=1;i<=n;i++) {
		dd[dfn[i]]=i;
	}
	build(1,1,n);
	for(int i = 1;i <= q;i++) {
		u=read();v=read();
		change(u,v);
		node a=ask(1,1,n,dfn[1],dfn[1]+len[1]-1);
		// cerr<<a.a[1][1]<<" "<<a.a[2][1]<<endl;
		write(max(a.a[1][1],a.a[2][1]));
		putchar('\n');
	}
	return 0;
}

不想写了

Hash On Tree

题目传送门
题解传送门

posted @ 2025-08-16 15:12  wjx_2010  阅读(7)  评论(1)    收藏  举报