万字长文·动态树分治/动态动态规划/ddp入门经典
万字长文·动态树分治/动态动态规划/ddp入门经典
这东西好像有人叫它ddp
其实应该是动态树上dp吧
这类题往往是一个画风正常的树上dp问题加上修改
并且修改次数极多
注意到树上dp往往一个点权的修改只会更改它到根的路径上的答案
所以我们可以每次在根的路径上求解
可通过一些弱数据
但复杂度不正确
考虑树链剖分
一条链上的转移可以写成矩阵快速幂
不过我们需要重新定义卷积
一道例题
设\(f_{n,0/1}\)表示以i为根的子树选不选根的最大值
发现dp方程
然后设\(g_{i,0/1}\) 为不考虑重儿子 选不选根的最大值
方程重写作
这样就规避了和式 并且根据树剖 只会修改重儿子 所以具有相当的稳定性
之后写出矩阵 易得
于是这样满足了链尾为叶子的性质
#include<bits/stdc++.h>
using namespace std;
#define N 200005
#define INF 0x3f3f3f3f
int n,m,w[N];
vector<int> mp[N];
struct matrix
{
int a[3][3],n,m;
void init(int r,int c)
{
n=r;
m=c;
for(int i=0;i<n;i++)
for(int j=0;j<m;j++)
a[i][j]=-INF;
}
matrix(int r=0,int c=0)
{
init(r,c);
}
matrix operator * (const matrix &k1)const
{
matrix ret(n,k1.m);
for(int k=0;k<k1.n;k++)
for(int i=0;i<n;i++)
for(int j=0;j<k1.m;j++)
ret.a[i][j]=
max(ret.a[i][j],a[i][k]+k1.a[k][j]);
return ret;
}
}val[N];
struct segtree
{
struct node
{
matrix mat;
int l,r;
}tr[N<<2];
void build(int x,int l,int r)
{
tr[x].l=l;tr[x].r=r;
if(l==r)
{
tr[x].mat=val[l];
return;
}
int mid=(l+r)>>1;
build(x<<1,l,mid),build(x<<1|1,mid+1,r);
tr[x].mat=tr[x<<1].mat*tr[x<<1|1].mat;
}
void change(int x,int p)
{
if(tr[x].l==p&&tr[x].r==p)
{
tr[x].mat=val[p];
return;
}
int mid=(tr[x].l+tr[x].r)>>1;
if(p<=mid) change(x<<1,p);
if(p>mid) change(x<<1|1,p);
tr[x].mat=tr[x<<1].mat*tr[x<<1|1].mat;
}
matrix query(int x,int l,int r)
{
if(l<=tr[x].l&&tr[x].r<=r)
return tr[x].mat;
int mid=(tr[x].l+tr[x].r)>>1;
if(r<=mid) return query(x<<1,l,r);
else if(l>mid) return query(x<<1|1,l,r);
else return query(x<<1,l,r)*query(x<<1|1,l,r);
}
}tre;
int idx=0,id[N],ed[N],dep[N],fa[N],son[N],top[N],size[N];
void dfs1(int x,int f,int deep)
{
dep[x]=deep;size[x]=1;fa[x]=f,size[son[x]=0]=-1;
for(int i=0;i<mp[x].size();i++)
{
int u=mp[x][i];
if(u==f) continue;
dfs1(u,x,deep+1);
size[x]+=size[u];
son[x]=size[u]>size[son[x]]?u:son[x];
}
}
int f[N][2],g[N][2];
void dfs2(int x,int topfa)
{
top[x]=topfa;ed[topfa]=x;id[x]=++idx;
g[x][0]=0,g[x][1]=w[x];
if(!son[x])
{
f[x][0]=0;f[x][1]=w[x];
val[id[x]].init(2,1);
val[id[x]].a[0][0]=f[x][0];
val[id[x]].a[1][0]=f[x][1];
return;
}
dfs2(son[x],topfa);
for(int i=0;i<mp[x].size();i++)
{
int v=mp[x][i];
if(v!=fa[x]&&v!=son[x])
dfs2(v,v),g[x][0]+=max(f[v][0],f[v][1]),
g[x][1]+=f[v][0];
}
f[x][0]=g[x][0]+max(f[son[x]][0],f[son[x]][1]);
f[x][1]=g[x][1]+f[son[x]][0];
val[id[x]].init(2,2);
val[id[x]].a[0][0]=val[id[x]].a[0][1]=g[x][0];
val[id[x]].a[1][0]=g[x][1];
}
void change(int x,int v)
{
val[id[x]].a[1][0]+=v-w[x];
int bp=x;
while(x)
{
matrix lst=tre.query(1,id[top[x]],id[ed[top[x]]]);
tre.change(1,id[x]);
matrix now=tre.query(1,id[top[x]],id[ed[top[x]]]);
x=fa[top[x]];
val[id[x]].a[0][0]+=max(now.a[0][0],now.a[1][0])-max(lst.a[0][0],lst.a[1][0]);
val[id[x]].a[0][1]=val[id[x]].a[0][0];
val[id[x]].a[1][0]+=now.a[0][0]-lst.a[0][0];
}
w[bp]=v;
}
int query(int x)
{
matrix ans=tre.query(1,id[x],id[ed[top[x]]]);
return max(ans.a[0][0],ans.a[1][0]);
}
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++)
cin>>w[i];
for(int i=1,x,y;i<=n-1;i++)
{
cin>>x>>y;
mp[x].push_back(y);
mp[y].push_back(x);
}
dfs1(1,0,1);
dfs2(1,1);
tre.build(1,1,n);
for(int i=1,x,y;i<=m;i++)
{
cin>>x>>y;
change(x,y);
cout<<query(1)<<endl;
}
return 0;
}