C76【模板】动态DP+LCT P4719 动态树分治

视频链接:262【模板】动态DP+LCT P4719 动态树分治_哔哩哔哩_bilibili

 

 

 

 

 

 

Luogu P4719【模板】"动态 DP"&动态树分治

// 动态DP+LCT O(nlogn)
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;

#define lc(x) ch[x][0]
#define rc(x) ch[x][1]
#define notroot(x) lc(fa[x])==x||rc(fa[x])==x
const int N=100010,inf=(1<<30);
int n,m,a[N],f[N][2];
vector<int>G[N];
int ch[N][2],fa[N]; //splay:ch儿,fa父
struct matrix{
  int g[2][2];
  matrix(){g[0][0]=g[0][1]=g[1][0]=g[1][1]=-inf;}
  matrix operator*(matrix b){
    matrix t;
    for(int i=0;i<2;++i)
    for(int j=0;j<2;++j)
    for(int k=0;k<2;++k)
      t.g[i][j]=max(t.g[i][j],g[i][k]+b.g[k][j]);
    return t;
  }
}mt[N],tr[N]; //mt:节点x的g矩阵, tr:节点x的矩阵积

void dfs(int x){ //预处理fa,f,mt,tr
  f[x][1]=a[x];
  for(int y:G[x]){
    if(y!=fa[x]){
      fa[y]=x;
      dfs(y);
      f[x][0]+=max(f[y][0],f[y][1]);
      f[x][1]+=f[y][0];
    }
  }
  mt[x].g[0][0]=mt[x].g[0][1]=f[x][0];
  mt[x].g[1][0]=f[x][1];
  tr[x]=mt[x]; //初始时每个点就是一个splay
}
void pushup(int x){ //上传
  tr[x]=mt[x];
  if(lc(x))tr[x]=tr[lc(x)]*tr[x];
  if(rc(x))tr[x]=tr[x]*tr[rc(x)]; //矩阵积(即和取最大)
}
void rotate(int x){ //旋转x
  int y=fa[x],z=fa[y],k=rc(y)==x; //y的右儿是x吗
  if(notroot(y)) ch[z][rc(z)==y]=x; fa[x]=z; //z的儿是x,x的父是z
  ch[y][k]=ch[x][k^1]; fa[ch[x][k^1]]=y; //y的儿是x的异儿,x的异儿的父是y
  ch[x][k^1]=y; fa[y]=x; //x的异儿是y,y的父是x
  pushup(y); pushup(x);  //自底向上pushup
}
void splay(int x){ //x伸展到根
  while(notroot(x)){ //折线转xx,直线转yx
    int y=fa[x],z=fa[y];
    if(notroot(y)) (rc(y)==x)^(rc(z)==y)?rotate(x):rotate(y);
    rotate(x);
  }
}
void access(int x){ //打通x到树根的路
  for(int y=0; x;){
    splay(x);  //x转到当前splay的根
    if(rc(x)){ //x右儿子即将变成虚儿子,加上其贡献
      mt[x].g[0][0]+=max(tr[rc(x)].g[0][0],tr[rc(x)].g[1][0]);
      mt[x].g[1][0]+=tr[rc(x)].g[0][0];
      mt[x].g[0][1]=mt[x].g[0][0];
    }
    if(y){    //y即将变成x的实儿子,减去其贡献
      mt[x].g[0][0]-=max(tr[y].g[0][0],tr[y].g[1][0]);
      mt[x].g[1][0]-=tr[y].g[0][0];
      mt[x].g[0][1]=mt[x].g[0][0];
    }
    rc(x)=y;   //把y变成x的右儿子(即x指向下面splay的根)
    pushup(x); //更新x
    x=fa[y=x]; //把x存于y,x继续爬到上面的splay
  }
}
void update(int x,int y){ //修改点权
  access(x); //通路
  splay(x);  //伸展
  mt[x].g[1][0]+=y-a[x]; //修改x点
  pushup(x); a[x]=y;
}
int main(){
  scanf("%d%d",&n,&m); int x,y;
  for(int i=1;i<=n;++i)scanf("%d",&a[i]);
  for(int i=1;i<n;i++){
    scanf("%d%d",&x,&y);
    G[x].push_back(y); G[y].push_back(x);
  }
  dfs(1); //预处理fa,f,mt,tr
  while(m--){
    scanf("%d%d",&x,&y);
    update(x,y);
    printf("%d\n",max(tr[x].g[0][0],tr[x].g[1][0]));
  } 
}

 

posted @ 2023-12-15 07:46  董晓  阅读(195)  评论(0编辑  收藏  举报