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; }