Luogu4719 【模板】"动态 DP"&动态树分治
https://www.luogu.com.cn/problem/P4719
动态DP
考虑静态的\(DP\)
设\(f_{u,0}\)表示\(u\)号点不取时,以\(u\)为子树的最大权独立集的权值,\(f_{u,1}\)表示\(u\)号点取时,以\(u\)为子树的最大权独立集的权值
\[f_{u,0}=\sum_{v \in son_u} \max(f_{v,0},f_{v,1})\\
f_{u,1}=\sum_{v \in son_u} f_{v,0}+val_u
\]
我们进行轻重链剖分,设\(w\)为\(u\)的重儿子
\(g_{u,0}\)表示\(u\)的轻儿子可取可不取,且\(u\)不能取的最大权值,\(g_{u,1}\)表示\(u\)的轻儿子不取,且\(u\)必取的最大权值
\[f_{u,0}=g_{u,0}+\max(f_{w,0},f_{w,1})\\
f_{u,1}=g_{u,1}+f_{w,0}
\]
为了加快运算,我们重新定义矩阵乘法的运算
\[C_{i,j}=\max(A_{i,k}+B_{k,j})
\]
转化式子
\[f_{u,0}=\max(g_{u,0}+f_{w,0},g_{u,0}+f_{w,1})\\
f_{u,1}=\max(g_{u,1}+f_{w,0},-\infty+f_{w,1})
\]
建立矩阵
\[\begin{vmatrix}
f_{w,0} & f_{w,1}
\end{vmatrix}
\times
\begin{vmatrix}
g_{u,0} & g_{u,0} \\
g_{u,1} & -\infty
\end{vmatrix}
=
\begin{vmatrix}
f_{u,0} & f_{u,1}
\end{vmatrix}
\]
由于\(dfn\)序中,叶子节点在后,我们转换一下运算顺序
\[\begin{vmatrix}
g_{u,0} & g_{u,0} \\
g_{u,1} & -\infty
\end{vmatrix}
\times
\begin{vmatrix}
f_{w,0} \\
f_{w,1}
\end{vmatrix}
=
\begin{vmatrix}
f_{u,0} \\
f_{u,1}
\end{vmatrix}
\]
叶子节点的矩阵\(f\)能够预处理
\[\begin{vmatrix}
0 \\
val_u
\end{vmatrix}
\]
为了方便,同样转换成\(2 \times 2\)的矩阵
\[\begin{vmatrix}
0 & -\infty \\
val_u & -\infty
\end{vmatrix}
\]
那么求一个子树\(u\)的最大权值,我们可以看到对于一个节点,我们维护的是\(g\)数组,那么它缺失的是重儿子的信息,所以我们要把\(u\)拉下去的重链上的矩阵相乘,取得的就是\(u\)的信息,所以我们需要记录每条重链的末端
考虑修改如何处理
对于一条重链上的点,直接单点修改,因为转移矩阵与重儿子无关
然后我们走到一条重链的顶端,对于它的父亲来说,这就是轻儿子了,我们直接除去原有的贡献,加入新的贡献即可
\(Code:\)
#include<iostream>
#include<cstdio>
#include<algorithm>
#define N 100005
#define M 200005
#define INF 1000000007
using namespace std;
int n,m,tot,x,y,cnt;
int fr[N],fa[N],ed[N],son[N],sz[N],T[N],dfn[N],id[N],q[N],nxt[M],d[M];
int ss[N];
int f[N][2],g[N][2];
struct mat
{
int r[2][2];
}kz[N],t[N << 2];
mat operator * (mat a,mat b)
{
mat c;
for (int i=0;i<2;i++)
for (int j=0;j<2;j++)
c.r[i][j]=-INF;
for (int k=0;k<2;k++)
for (int i=0;i<2;i++)
for (int j=0;j<2;j++)
c.r[i][j]=max(c.r[i][j],a.r[i][k]+b.r[k][j]);
return c;
}
void add(int x,int y)
{
tot++;
d[tot]=y;
nxt[tot]=fr[x];
fr[x]=tot;
}
void dfs1(int u)
{
int mx=-1;
sz[u]=1;
for (int i=fr[u];i;i=nxt[i])
{
int v=d[i];
if (v==fa[u])
continue;
fa[v]=u;
dfs1(v);
sz[u]+=sz[v];
if (sz[v]>mx)
{
mx=sz[v];
son[u]=v;
}
}
}
void dfs2(int u,int tp)
{
g[u][0]=0,g[u][1]=q[u];
dfn[++cnt]=u;
id[u]=cnt;
T[u]=tp;
if (!son[u])
{
int v=u;
while (v!=tp)
{
ed[v]=id[u];
v=fa[v];
}
ed[v]=id[u];
kz[u].r[0][0]=g[u][0];
kz[u].r[1][0]=g[u][1];
kz[u].r[0][1]=kz[u].r[1][1]=-INF;
f[u][0]=0,f[u][1]=q[u];
return;
}
dfs2(son[u],tp);
for (int i=fr[u];i;i=nxt[i])
{
int v=d[i];
if (v==fa[u] || v==son[u])
continue;
dfs2(v,v);
g[u][0]+=max(f[v][0],f[v][1]);
g[u][1]+=f[v][0];
}
f[u][0]=g[u][0]+max(f[son[u]][0],f[son[u]][1]);
f[u][1]=g[u][1]+f[son[u]][0];
kz[u].r[0][0]=kz[u].r[0][1]=g[u][0];
kz[u].r[1][0]=g[u][1];
kz[u].r[1][1]=-INF;
}
#define ls (p << 1)
#define rs ((p << 1) | 1)
void build(int p,int l,int r)
{
if (l==r)
{
t[p]=kz[dfn[l]];
ss[l]=p;
return;
}
int mid=(l+r) >> 1;
build(ls,l,mid);
build(rs,mid+1,r);
t[p]=t[ls]*t[rs];
}
void change(int p)
{
if (!p)
return;
t[p]=t[ls]*t[rs];
change(p >> 1);
}
mat calc(int p,int l,int r,int x,int y)
{
if (l==x && r==y)
return t[p];
int mid=(l+r) >> 1;
if (y<=mid)
return calc(ls,l,mid,x,y); else
if (x>mid)
return calc(rs,mid+1,r,x,y); else
return calc(ls,l,mid,x,mid)*calc(rs,mid+1,r,mid+1,y);
}
void update(int x,int y)
{
kz[x].r[1][0]+=y-q[x];
q[x]=y;
while (x)
{
mat bef,aft;
bef=calc(1,1,n,id[T[x]],ed[T[x]]);
t[ss[id[x]]]=kz[x];
change(ss[id[x]] >> 1);
aft=calc(1,1,n,id[T[x]],ed[T[x]]);
int u=fa[T[x]];
if (!u)
break;
kz[u].r[0][0]-=max(bef.r[0][0],bef.r[1][0]);
kz[u].r[0][0]+=max(aft.r[0][0],aft.r[1][0]);
kz[u].r[0][1]=kz[u].r[0][0];
kz[u].r[1][0]-=bef.r[0][0];
kz[u].r[1][0]+=aft.r[0][0];
x=u;
}
}
int main()
{
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++)
scanf("%d",&q[i]);
for (int i=1;i<n;i++)
{
scanf("%d%d",&x,&y);
add(x,y);
add(y,x);
}
dfs1(1);
dfs2(1,1);
build(1,1,n);
for (int i=1;i<=m;i++)
{
scanf("%d%d",&x,&y);
update(x,y);
mat ans=calc(1,1,n,id[1],ed[1]);
printf("%d\n",max(ans.r[0][0],ans.r[1][0]));
}
return 0;
}