Dynamic DP is a technique introduced by Cat Kun at WC2018, generally used to solve DP problems on trees with point weight (or edge weight) modification operations.
Example
Let's explain the process of dynamic DP using this template problem as an example.
Given a tree with \(n\) vertices, each vertex has a weight. There are \(m\) operations, each operation gives \(x,y\) indicating that the weight of vertex \(x\) is modified to \(y\). After each operation, you need to output the weight of the maximum weight independent set of the tree.
First, perform heavy-light decomposition on the tree. Suppose we have a heavy chain like this:
Let \(g_{i,0}\) denote the maximum answer when not selecting \(i\) and only allowing selection from the subtrees of \(i\)'s light children, and \(g_{i,1}\) denote the maximum answer when selecting \(i\) without considering \(son_i\), where \(son_i\) is the heavy child of \(i\).
Assuming we know \(g_{i,0/1}\), then the DP equations are:
Note that we are using the generalized multiplication rule here.
It can be observed that during modification operations, we only need to update \(g_{i,1}\) and each upward heavy chain.
Detailed Approach
Perform DFS preprocessing to compute \(f_{i,0/1}\) and \(g_{i,0/1}\).
Perform heavy-light decomposition on the tree (note: since querying a point requires computing the interval matrix product from that point to the end of its heavy chain, record \(End_i\) for each point, representing the end node ID of the heavy chain containing \(i\)). Build a segment tree for each heavy chain, maintaining the \(g\) matrix and the interval product of \(g\) matrices.
During modification, first update \(g_{i,1}\) and the matrix at node \(i\) in the segment tree, compute the change in the matrix of \(top_i\), and propagate the modification to the matrix of \(fa_{top_i}\).
Querying involves computing the interval product from 1 to the end of its heavy chain, and finally taking a \(\max\).
```cpp#include<algorithm>#include<cstring>#include<iostream>usingnamespacestd;constexprintMAXN=500010;constexprintINF=0x3f3f3f3f;intBegin[MAXN],Next[MAXN],To[MAXN],e,n,m;intsz[MAXN],son[MAXN],top[MAXN],fa[MAXN],dis[MAXN],p[MAXN],id[MAXN],End[MAXN];// p[i] represents the new index of node i after tree decomposition, id[p[i]] = iintcnt,tot,a[MAXN],f[MAXN][2];structmatrix{intg[2][2];matrix(){memset(g,0,sizeof(g));}matrixoperator*(constmatrix&b)const// Overload matrix multiplication{matrixc;for(inti=0;i<=1;i++)for(intj=0;j<=1;j++)for(intk=0;k<=1;k++)c.g[i][j]=max(c.g[i][j],g[i][k]+b.g[k][j]);returnc;}}Tree[MAXN],g[MAXN];// Tree[] is the built segment tree, g[] is the matrix maintained for each nodevoidPushUp(introot){Tree[root]=Tree[root<<1]*Tree[root<<1|1];}voidBuild(introot,intl,intr){if(l==r){Tree[root]=g[id[l]];return;}intMid=(l+r)>>1;Build(root<<1,l,Mid);Build(root<<1|1,Mid+1,r);PushUp(root);}matrixQuery(introot,intl,intr,intL,intR){if(L<=l&&r<=R)returnTree[root];intMid=(l+r)>>1;if(R<=Mid)returnQuery(root<<1,l,Mid,L,R);if(Mid<L)returnQuery(root<<1|1,Mid+1,r,L,R);returnQuery(root<<1,l,Mid,L,R)*Query(root<<1|1,Mid+1,r,L,R);// Pay attention to the writing of the query operation}voidModify(introot,intl,intr,intpos){if(l==r){Tree[root]=g[id[l]];return;}intMid=(l+r)>>1;if(pos<=Mid)Modify(root<<1,l,Mid,pos);elseModify(root<<1|1,Mid+1,r,pos);PushUp(root);}voidUpdate(intx,intval){g[x].g[1][0]+=val-a[x];a[x]=val;// First modify the g matrix of xwhile(x){matrixlast=Query(1,1,n,p[top[x]],End[top[x]]);// Query the original g matrix of top[x]Modify(1,1,n,p[x]);// Perform the modification (the g matrix of point x has been modified but not yet updated in the segment tree)matrixnow=Query(1,1,n,p[top[x]],End[top[x]]);// Query the new g matrix of top[x]x=fa[top[x]];g[x].g[0][0]+=max(now.g[0][0],now.g[1][0])-max(last.g[0][0],last.g[1][0]);g[x].g[0][1]=g[x].g[0][0];g[x].g[1][0]+=now.g[0][0]-last.g[0][0];// Modify the g matrix of fa[top[x]] based on the change}}voidadd(intu,intv){To[++e]=v;Next[e]=Begin[u];Begin[u]=e;}voidDFS1(intu){sz[u]=1;intMax=0;f[u][1]=a[u];for(inti=Begin[u];i;i=Next[i]){intv=To[i];if(v==fa[u])continue;dis[v]=dis[u]+1;fa[v]=u;DFS1(v);sz[u]+=sz[v];if(sz[v]>Max){Max=sz[v];son[u]=v;}f[u][1]+=f[v][0];f[u][0]+=max(f[v][0],f[v][1]);// Calculate f[i][0/1] during DFS1}}voidDFS2(intu,intt){top[u]=t;p[u]=++cnt;id[cnt]=u;End[t]=cnt;g[u].g[1][0]=a[u];g[u].g[1][1]=-INF;if(!son[u])return;DFS2(son[u],t);for(inti=Begin[u];i;i=Next[i]){intv=To[i];if(v==fa[u]||v==son[u])continue;DFS2(v,v);g[u].g[0][0]+=max(f[v][0],f[v][1]);g[u].g[1][0]+=f[v][0];// Calculate the g matrix based on f[i][0/1]}g[u].g[0][1]=g[u].g[0][0];}intmain(){cin.tie(nullptr)->sync_with_stdio(false);cin>>n>>m;for(inti=1;i<=n;i++)cin>>a[i];for(inti=1;i<=n-1;i++){intu,v;cin>>u>>v;add(u,v);add(v,u);}dis[1]=1;DFS1(1);DFS2(1,1);Build(1,1,n);for(inti=1;i<=m;i++){intx,val;cin>>x>>val;Update(x,val);matrixans=Query(1,1,n,1,End[1]);// Query the matrix multiplication of the heavy chain containing node 1cout<<max(ans.g[0][0],ans.g[1][0])<<'\n';}return0;}