【模板】"动态 DP"&动态树分治

CLII.【模板】"动态 DP"&动态树分治

裸的树上最大独立集怎么做?设 \(f_{x,0}\) 表示在 \(x\) 子树中, \(x\) 不选的最大答案;\(f_{x,1}\) 表示在 \(x\) 子树中,\(x\) 选的最大答案。则有

\[f_{x,0}=\sum\limits_{y\in\text{son}_x}\max(f_{y,0},f_{y,1}) \]

\[f_{x,1}=a_x+\sum\limits_{y\in\text{son}_x}f_{y,0} \]

现在要支持修改,怎么办?

发现,每次修改,DP值受到影响的位置是从修改位置到根的一条路径。听上去像是树剖应用的场景?

于是我们考虑树剖,并设 \(g_{x,0/1}\) 表示忽略 \(x\) 的重儿子时的答案。则,\(f_{x,0}=\max(f_{y,0},f_{y,1})+g_{x,0},f_{x,1}=f_{y,0}+g_{x,1}\),其中 \(y\)\(x\) 的重儿子。

现在,我们考虑将其转成矩阵的形式。则有

\[\begin{bmatrix}f_{y,0}\\f_{y,1}\end{bmatrix}\begin{bmatrix}g_{x,0}&g_{x,1}\\g_{x,0}&-\infty\end{bmatrix}=\begin{bmatrix}f_{x,0}\\f_{x,1}\end{bmatrix} \]

其中,矩阵乘法的定义是 \(uv=w\Leftrightarrow w_{i,j}=\max\limits_k\{u_{i,k}+v_{k,j}\}\)。手推可得其满足结合律。

于是,我们考虑从一个叶子的 \(f\) 数组开始(令人惊喜的是,叶子节点的 \(f\)\(g\) 是相等的(因为其不存在儿子),这使得转移很方便),一路往上乘矩阵,即可得到该叶子所在重链上任意节点的 \(f\) 数组。并且,因为任意重链的结尾节点都是叶子,所以通过上述方法可以求出任意节点的 \(f\) 数组。

现在考虑带修。发现,一次修改只会影响到根的路径上所有重链顶的父亲的 \(g\) 数组,而这样的父亲最多只有 \(O(\log n)\) 个,故直接暴力跳链修改父亲即可。

时间复杂度 \(O(n\log^2n)\)

代码:

#include<bits/stdc++.h>
using namespace std;
const int inf=0xd0d0d0d0;
int n,m,a[100100],f[100100][2],g[100100][2];//0:self unchosen 1:self chosen
int fa[100100],son[100100],sz[100100],dfn[100100],rev[100100],top[100100],bot[100100],tot;
vector<int>v[100100];
void dfs1(int x){
	sz[x]=1,f[x][0]=0,f[x][1]=a[x];
	for(auto y:v[x])if(y!=fa[x]){
		fa[y]=x,dfs1(y),sz[x]+=sz[y];
		if(sz[son[x]]<sz[y])son[x]=y;
		f[x][0]+=max(f[y][0],f[y][1]),f[x][1]+=f[y][0];
	}
}
void dfs2(int x){
	dfn[x]=++tot,rev[tot]=x;if(!top[x])top[x]=x;if(son[x])top[son[x]]=top[x],dfs2(son[x]);else bot[top[x]]=x;
	for(auto y:v[x])if(y!=fa[x]&&y!=son[x])dfs2(y),g[x][0]+=max(f[y][0],f[y][1]),g[x][1]+=f[y][0];
}
#define lson x<<1
#define rson x<<1|1
#define mid ((l+r)>>1)
struct Matrix{
	int t[2][2];
	Matrix(){memset(t,inf,sizeof(t));}
	Matrix(int x){t[1][0]=t[0][1]=inf,t[0][0]=t[1][1]=0;}
	int*operator[](const int&x){return t[x];}
	friend Matrix operator*(Matrix u,Matrix v){Matrix w;for(int i=0;i<2;i++)for(int j=0;j<2;j++)for(int k=0;k<2;k++)w[i][j]=max(w[i][j],u[i][k]+v[k][j]);return w;}
	void print()const{for(int i=0;i<2;i++,puts(""))for(int j=0;j<2;j++)printf("%d ",t[i][j]);}
	int zero(){return max(max(t[0][0],t[1][0]),max(t[0][1],t[1][1]));}
	int one(){return max(t[0][0],t[1][0]);}
}seg[400100];
Matrix Generate(int x){Matrix M;M[0][0]=g[x][0],M[1][0]=g[x][0],M[0][1]=g[x][1],M[1][1]=inf;return M;}
void build(int x,int l,int r){if(l==r)seg[x]=Generate(rev[l]);else build(lson,l,mid),build(rson,mid+1,r),seg[x]=seg[rson]*seg[lson];}
Matrix query(int x,int l,int r,int L,int R){if(l>R||r<L)return Matrix(0);if(L<=l&&r<=R)return seg[x];return query(rson,mid+1,r,L,R)*query(lson,l,mid,L,R);}
void reset(int x,int l,int r,int P){if(l>P||r<P)return;if(l==r)seg[x]=Generate(rev[P]);else reset(lson,l,mid,P),reset(rson,mid+1,r,P),seg[x]=seg[rson]*seg[lson];}
int modify(int x,int y){
	Matrix M;
	for(int i=x;top[i]!=1;)M=query(1,1,n,dfn[top[i]],dfn[bot[top[i]]]),i=fa[top[i]],g[i][0]-=M.zero(),g[i][1]-=M.one();
	g[x][1]+=y-a[x],a[x]=y,reset(1,1,n,dfn[x]);
	for(int i=x;top[i]!=1;)M=query(1,1,n,dfn[top[i]],dfn[bot[top[i]]]),i=fa[top[i]],g[i][0]+=M.zero(),g[i][1]+=M.one(),reset(1,1,n,dfn[i]);
	M=query(1,1,n,1,dfn[bot[1]]);
	return M.zero();
}
int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)scanf("%d",&a[i]),g[i][1]=a[i];
	for(int i=1,x,y;i<n;i++)scanf("%d%d",&x,&y),v[x].push_back(y),v[y].push_back(x);
	dfs1(1),dfs2(1),build(1,1,n);
	for(int i=1,x,y;i<=m;i++)scanf("%d%d",&x,&y),printf("%d\n",modify(x,y));
	return 0;
}

posted @ 2021-03-31 15:37  Troverld  阅读(45)  评论(0编辑  收藏  举报