luoguP4719 【模板】动态 DP

题目描述

给定一棵n个点的树,点带点权。

m次操作,每次操作给定x,y,表示修改点x的权值为y

你需要在每次操作之后求出这棵树的最大权独立集的权值大小。

输入输出格式

输入格式:

 

第一行,n,m分别代表点数和操作数。

第二行,V1,V2,...,Vn,代表n个点的权值。

接下来n−1行,x,y,描述这棵树的n−1条边。

接下来m行,x,y,修改点x的权值为y

 

输出格式:

 

对于每个操作输出一行一个整数,代表这次操作后的树上最大权独立集。

保证答案在int范围内

 

 

动态DP讲解:
 
考虑最大独立集转移方程:

$F[u][0]$ 表示 $u$ 点为根,不选$u$点的最大独立集.

$F[u][1]$ 表示 $u$ 点为根,选$u$点的最大独立集.

转移:

$F[u][0]=\sum_{v \in son[u]}max(F[u][0],F[u][1])$

$F[u][1]=\sum_{v \in son[u]}F[u][0]$

不难发现,每当我们修改一个点的点权,我们只会影响到该点到根节点这条链上的DP值.

如果树的高度是 $log(n)$,那么暴力跳链进行修改是完全可行的.

然而,题中树的高度可能会被恶意卡成 $O(n)$ 级别的.

那么,暴力跳链的总体复杂度就退化为 $O(nm)$ 了.

我们需要一个优秀的做法,使得每次修改都不受树高影响.

方法一 :  树链剖分 + 线段树 + 矩阵乘法.

注意的是,我们这里的矩阵乘法是长这样的:

 
Matrix operator*(Matrix a,Matrix b)
{
    Matrix c; 
    c[0][0]=max(a[0][0]+b[0][0],a[0][1]+b[1][0]); 
    c[0][1]=max(a[0][0]+b[0][1],a[0][1]+b[1][1]); 
    c[1][0]=max(a[1][0]+b[0][0],a[1][1]+b[1][0]); 
    c[1][1]=max(a[1][0]+b[0][1],a[1][1]+b[1][1]);      
    return c; 
}

 

我们用矩阵乘法维护每一次链上的转移.

 

我们用矩阵乘法维护每一次链上的转移.

根据常识,矩阵乘法要求满足结合律,而每一次状态转移也是满足结合律的.

对于每一个点开一个矩阵 $M_{u}$.

 $M_{u}=\begin{bmatrix} F_{u,0} & F_{u,0}\\ F_{u,1}  & -\infty \end{bmatrix}$

 考虑已经得到一个点 $i$ 的 $F_{i,0}$ 及 $F_{i,1}$,以及 $fa_{i}$ 的 $M_{fa_{i}}$.
 
 我们就可以通过矩阵乘法转移到 $fa_{i}$ 的方程了 !

 设 $u=fa_{i}$

 即 $\begin{bmatrix} F_{u,0} & F_{u,0}\\ F_{u,1}  & -\infty \end{bmatrix}\times\begin{bmatrix} F_{i,0}\\F_{i,1} \end{bmatrix}=\begin{bmatrix} F_{u,0}\\F_{u,1} \end{bmatrix}$

 是不是十分巧妙 ? (注:矩阵中的 $F[u][0]$ 与 $F[u][1]$ 指的是不包括该点重儿子的DP值,$i$ 是该点的重儿子,我们后文会提及)

 优秀的是,这些矩阵转移是由线段树来维护的.

 准确来说,重链上的线段树.

 我们引入树链剖分.
  
 
 
 
    
 
 
 
 考虑树剖的原理:

树剖将树剖成不超过 $log(n)$ 个重链,而树上任意两点最终都会蹦到同一个重链上.

 最坏也只会跳 $log(n)$ 次链顶,所以时间复杂度是十分优秀的. 

 而在动态 DP 中,我们也是用蹦链的方式来实现.

 即每条重链用一颗线段树维护,线段树来维护一段区间的矩阵乘法.

 这样,每次修改与查询就在对应的线段树中进行.
 
考虑修改:

在上文中,我们已经知道,每次修改只会影响到该点到根节点的一条路径,我们只需修改该点到根节点这条链上的信息即可.

现在在一条重链上的线段树进行单点修改,这是很简单的.

然后,我们再跳到该点所属重链的父亲上(设为 $fa$)

由于修改的是轻儿子,所以 $fa$ 的 $F[u][0]$ 与 $F[u][1]$ 都需要改变.
 
考虑查询:

由于我们在修改时已经将所有信息全部修改完毕,只需调用出根节点所在的重链的线段树, 并查一下区间矩阵乘法的结果即可.
 

Code: 

#include<bits/stdc++.h> 
#define setIO(s) freopen(s".in","r",stdin) 
#define maxn 300000 
#define lson (now<<1) 
#define rson ((now<<1)|1) 
#define ll long long 
const ll inf = 1e17; 
using namespace std;
int hd[maxn],to[maxn],nex[maxn],V[maxn],hson[maxn],fa[maxn],dfn[maxn], ln[maxn],F[maxn][2],siz[maxn],top[maxn],bot[maxn]; 
int edges,tim,n,Q; 
void add(int u,int v)
{
	nex[++edges]=hd[u],hd[u]=edges,to[edges]=v; 
}
void dfs1(int u,int ff)
{
	siz[u]=1,fa[u]=ff; 
	for(int i=hd[u];i;i=nex[i])
	{
		if(to[i]==ff) continue; 
		dfs1(to[i],u); 
		siz[u]+=siz[to[i]]; 
		if(siz[to[i]] > siz[hson[u]]) hson[u]=to[i]; 
	}
}
void dfs2(int u,int tp)
{
	top[u]=tp,ln[++tim]=u,dfn[u]=tim; 
	if(hson[u]) 
		dfs2(hson[u], tp), bot[u]=bot[hson[u]]; 
	else 
		bot[u]=u; 
	for(int i=hd[u];i;i=nex[i])
	{
		if(to[i]==fa[u]||to[i]==hson[u]) continue; 
		dfs2(to[i],to[i]);  
	}
}
void dfs(int u)
{
	F[u][0]=0,F[u][1]=V[u]; 
	for(int i=hd[u];i;i=nex[i])
	{
		if(to[i]==fa[u]) continue; 
		dfs(to[i]); 
		F[u][0]+=max(F[to[i]][1],F[to[i]][0]); 
		F[u][1]+=F[to[i]][0]; 
	}
}
struct Matrix
{
	ll a[2][2]; 
	ll*operator[](int x){ return a[x]; }
}t[maxn<<1],tmp[maxn<<1]; 
Matrix operator*(Matrix a,Matrix b)
{
	Matrix c; 
	c[0][0]=max(a[0][0]+b[0][0],a[0][1]+b[1][0]); 
	c[0][1]=max(a[0][0]+b[0][1],a[0][1]+b[1][1]); 
	c[1][0]=max(a[1][0]+b[0][0],a[1][1]+b[1][0]); 
	c[1][1]=max(a[1][0]+b[0][1],a[1][1]+b[1][1]);      
	return c; 
}
void Build(int now,int l,int r)
{
	if(l==r)
	{
		int u=ln[l];  
		ll g0=0,g1=V[u]; 
		for(int i=hd[u];i;i=nex[i])
		{
			int v=to[i];
			if(v==fa[u]||v==hson[u]) continue; 
			g0+=max(F[v][0],F[v][1]); 
			g1+=F[v][0]; 
		}
		t[now]=tmp[l]=(Matrix) {g0,g0,g1,-inf}; 
		return; 
	}
	int mid=(l+r)>>1; 
	Build(lson,l,mid),Build(rson,mid+1,r); 
	t[now]=t[lson]*t[rson]; 
}
void Modify(int now,int l,int r,int p)
{
	if(l==r) 
	{
	    t[now]=tmp[l]; 
	    return;  
	}
	int mid=(l+r)>>1; 
	if(p<=mid) Modify(lson,l,mid,p); 
	else Modify(rson,mid+1,r,p); 
	t[now]=t[lson]*t[rson]; 
}
Matrix Query(int now,int l,int r,int L,int R)
{
	if(L==l&&r==R) return t[now];
	int mid=(l+r)>>1; 
	if(R<=mid) return Query(lson,l,mid,L,R); 
	if(L>mid) return Query(rson,mid+1,r,L,R); 
	return Query(lson,l,mid,L,mid)*Query(rson,mid+1,r,mid+1,R); 
}
void Update(int u,int w)
{
	tmp[dfn[u]][1][0]+=w-V[u],V[u]=w; 
	while(u)
	{
		Matrix a=Query(1,1,n,dfn[top[u]],dfn[bot[u]]); 
		Modify(1,1,n,dfn[u]); 
		Matrix b=Query(1,1,n,dfn[top[u]],dfn[bot[u]]); 
		u=fa[top[u]];
		if(!u) break; 
		int x = dfn[u]; 
		ll g0=a[0][0],g1=a[1][0],f0=b[0][0],f1=b[1][0]; 
		tmp[x][0][0]=tmp[x][0][1]=tmp[x][0][0]+max(f0,f1)-max(g0,g1); 
		tmp[x][1][0]=tmp[x][1][0]+f0-g0; 
	}
}
int main()
{
	// setIO("input"); 
	scanf("%d%d",&n,&Q); 
	for(int i=1;i<=n;++i) scanf("%d",&V[i]); 
	for(int i=1,u,v;i<n;++i) 
	{
		scanf("%d%d",&u,&v),add(u,v),add(v,u); 
	}
	dfs1(1,0),dfs2(1,1),dfs(1),Build(1,1,n); 
	while(Q--)
	{
		int x,w; 
		scanf("%d%d",&x,&w);
		Update(x,w); 
		Matrix ans=Query(1,1,n,dfn[1],dfn[bot[1]]);                
		printf("%lld\n",max(ans[0][0],ans[1][0])); 
	}
	return 0; 
}

  

posted @ 2019-06-03 09:43  EM-LGH  阅读(215)  评论(0编辑  收藏  举报