万字长文·动态树分治/动态动态规划/ddp入门经典

万字长文·动态树分治/动态动态规划/ddp入门经典

这东西好像有人叫它ddp

其实应该是动态树上dp吧

这类题往往是一个画风正常的树上dp问题加上修改

并且修改次数极多

注意到树上dp往往一个点权的修改只会更改它到根的路径上的答案

所以我们可以每次在根的路径上求解

可通过一些弱数据

但复杂度不正确

考虑树链剖分

一条链上的转移可以写成矩阵快速幂

不过我们需要重新定义卷积


一道例题

P4719

\(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;
}
posted @ 2021-06-16 21:10  禁止右转  阅读(92)  评论(0编辑  收藏  举报