动态DP-学习笔记

i207M

2018-11-13 08:35:16

Personal

## 动态DP 猫锟在WC2018讲的黑科技——动态DP,就是一个画风正常的DP问题再加上一个动态修改操作。 By RabbitHu. 重定义矩阵乘法: $C_{i, j} = \max_{k = 1}^{n} (A_{i, k} + B_{k, j})$ 类似于乘法和加法的五种运算律,这两种变化也满足“加法交换律”、“加法结合律”、“max交换律”、“max结合律”和“加法分配律“。那么这种矩阵乘法显然也满足矩阵乘法结合律,就像正常的矩阵乘法一样,可以用线段树维护。 ## 树上最大带权独立集 $f_{i,0}$表示子树i中不选i的最大权独立集大小,$f_{i,1}$表示子树i中选i的最大权独立集大小。 但这是动态DP,我们需要树链剖分。假设我们已经完成了树链剖分,剖出来的某条重链看起来就像这样,右边的是在树上深度较大的点: ![img](https://cdn.luogu.com.cn/upload/pic/20275.png) 此时,比这条重链的top深度大且不在这条重链上的点的DP值都是已经求出来的(这可以做到)。我们**把它们的贡献,都统一于它们在这条重链上对应的那个祖先上**。 具体来说,设$g_{i,0}$表示不选i时,i不在链上的子孙的最大权独立集大小,$g_{i,1}$表示选i时,i不在链上的子孙再加上i自己的最大权独立集大小。 假如i右面的点是i+1,那么可以得出: $f_{i, 0} = g_{i, 0} + \max(f_{i + 1, 0}, f_{i + 1, 1})$ $f_{i, 1} = g_{i, 1} + f_{i + 1, 0}$ 矩阵: $\begin{bmatrix}g_{i, 0} & g_{i, 0} \\g_{i, 1} & 0\end{bmatrix} * \begin{bmatrix}f_{i + 1, 0} \\ f_{i + 1, 1}\end{bmatrix} = \begin{bmatrix}f_{i, 0} \\ f_{i, 1}\end{bmatrix}$ 那么基本思路就很清楚了:树剖,维护区间矩阵乘积。修改的时候,对于被修改节点到根节点路径上的每个重链(由下到上),先进行单点修改,然后求出这条重链的fa[top]在修改之后的g值,然后继续修改fa[top]所在重链。 ## 动态DP加强版 要想通过这道题,我们需要建立“全局平衡二叉树”,具体来说,我们对每条重链单独建二叉树,每个点的权值为轻儿子sz之和+1,每次找到带权重心,分治下去。 这样的复杂度可以类比LCT,会比较快。 ---------------- ```cpp namespace i207M { template<class T>il void in(T &x) { x=0; bool f=0; char c=getchar(); while(c<'0'||c>'9') { if(c=='-') f=1; c=getchar(); } while(c>='0'&&c<='9') x=x*10+(c^'0'),c=getchar(); if(f) x=-x; } #define N 100005 #define M N<<2 int n; int a[N]; int v[N<<1],nx[N<<1]; int head[N],cnte; il void adde(int uu,int vv) { v[++cnte]=vv,nx[cnte]=head[uu],head[uu]=cnte; } struct Mat { int m[2][2]; int *operator[](int x) { return m[x]; } Mat() { memset(m,0,sizeof m); } friend Mat operator*(const Mat &a,const Mat &b) { Mat res; for(ri i=0; i<2; ++i) for(ri k=0; k<2; ++k) for(ri j=0; j<2; ++j) res[i][j]=max(res[i][j],a.m[i][k]+b.m[k][j]); return res; } } val[N],dat[M]; int fa[N],sz[N],hsn[N],dfn[N],dk,ot[N],ys[N],top[N]; int f[N][2]; void dfs(int x,int ff) { fa[x]=ff,sz[x]=1; f[x][1]=a[x]; for(ri i=head[x]; i; i=nx[i]) if(v[i]!=ff) { dfs(v[i],x); sz[x]+=sz[v[i]]; if(sz[v[i]]>sz[hsn[x]]) hsn[x]=v[i]; f[x][0]+=max(f[v[i]][0],f[v[i]][1]); f[x][1]+=f[v[i]][0]; } } void efs(int x,int tp) { dfn[x]=++dk,ys[dk]=x,top[x]=tp,ot[tp]=dk; if(hsn[x]) efs(hsn[x],tp); for(ri i=head[x]; i; i=nx[i]) if(v[i]!=fa[x]&&v[i]!=hsn[x]) efs(v[i],v[i]); } const int rt=1; #define gm int mid((l+r)>>1) #define ls (x<<1) #define rs (x<<1|1) il void up(int x) { dat[x]=dat[ls]*dat[rs]; // WARNING: Lson, Rson } void build(int x,int l,int r) { if(l==r) { int u=ys[l],g0=0,g1=a[u]; for(ri i=head[u]; i; i=nx[i]) if(v[i]!=fa[u]&&v[i]!=hsn[u]) { g0+=max(f[v[i]][0],f[v[i]][1]); g1+=f[v[i]][0]; } val[l][0][0]=val[l][0][1]=g0,val[l][1][0]=g1,val[l][1][1]=-1000000000; dat[x]=val[l]; return; } gm; build(ls,l,mid), build(rs,mid+1,r); up(x); } void upd(int x,int l,int r,int p) { if(l==r) { dat[x]=val[l]; return; } gm; if(p<=mid) upd(ls,l,mid,p); else upd(rs,mid+1,r,p); up(x); } Mat ask(int x,int l,int r,int ql,int qr) { if(ql<=l&&r<=qr) return dat[x]; gm; if(qr<=mid) return ask(ls,l,mid,ql,qr); else if(ql>mid) return ask(rs,mid+1,r,ql,qr); else return ask(ls,l,mid,ql,qr)*ask(rs,mid+1,r,ql,qr); } Mat query(int x) { return ask(rt,1,n,dfn[x],ot[x]); } void pathupd(int u,int k) { val[dfn[u]][1][0]+=k-a[u]; a[u]=k; Mat od,nw; while(u) { od=query(top[u]); upd(rt,1,n,dfn[u]); nw=query(top[u]); u=fa[top[u]]; val[dfn[u]][0][0]+=max(nw[0][0],nw[1][0])-max(od[0][0],od[1][0]); val[dfn[u]][0][1]=val[dfn[u]][0][0]; val[dfn[u]][1][0]+=nw[0][0]-od[0][0]; } } void init() { dfs(1,0); efs(1,1); build(rt,1,n); } signed main() { #ifdef M207 freopen("in.in","r",stdin); #endif int cntq,x,y; in(n),in(cntq); for(ri i=1; i<=n; ++i) in(a[i]); for(ri i=1,a,b; i<n; ++i) { in(a),in(b); adde(a,b), adde(b,a); } init(); Mat t; while(cntq--) { in(x),in(y); pathupd(x,y); t=query(1); printf("%d\n",max(t[0][0],t[1][0])); } return 0; } } ```