上文中,我们用线段树来维护重链上的各种矩阵转移.
第二种方法是将树链剖分替换为动态树.
我们知道,矩阵乘法 $\begin{bmatrix} F_{u,0} & F_{u,0}\\ F_{u,1} & -\infty \end{bmatrix}\times\begin{bmatrix} F_{i,0}\\F_{i,1} \end{bmatrix}=\begin{bmatrix} F_{u,0}\\F_{u,1} \end{bmatrix}$ 中第一个矩阵中的每一个 $F_{u,k}$ 都是指考虑轻链时 $u$ 的 DP 值.
在 $LCT$ 中,树的轻重路径是交替变换的.
我们用维护子树信息的方式维护即可.
即设 $tmp$ 矩阵表示该点虚儿子的所有 DP 值的贡献(只是不包括重儿子).
再设 $t$ 矩阵维护 $Splay$ 中的转移.
具体细节看看代码:
Code:
// luogu-judger-enable-o2 //Dynamic DP with LCT #include<bits/stdc++.h> #define ll long long #define setIO(s) freopen(s".in","r",stdin) #define maxn 100002 #define inf 100000000 using namespace std; //Link cut tree void de() { printf("ok\n"); } namespace LCT { struct Matrix { ll a[2][2]; ll*operator[](int x){ return a[x];} }t[maxn],tmp[maxn]; Matrix operator*(Matrix a,Matrix b) { Matrix c; c[0][0]=max(a[0][0]+b[0][0],a[0][1]+b[1][0]); c[0][1]=max(a[0][0]+b[0][1],a[0][1]+b[1][1]); c[1][0]=max(a[1][0]+b[0][0],a[1][1]+b[1][0]); c[1][1]=max(a[1][0]+b[0][1],a[1][1]+b[1][1]); return c; } //tmp :: 虚儿子信息 //t :: 树剖实际转移矩阵 #define lson ch[x][0] #define rson ch[x][1] int ch[maxn][2],f[maxn]; int isRoot(int x) { return !(ch[f[x]][0]==x || ch[f[x]][1]==x); } int get(int x) { return ch[f[x]][1]==x; } void pushup(int x) { t[x]=tmp[x]; if(lson) t[x]=t[lson]*t[x]; if(rson) t[x]=t[x]*t[rson]; } void rotate(int x) { int old=f[x],fold=f[old],which=get(x); if(!isRoot(old)) ch[fold][ch[fold][1]==old]=x; ch[old][which]=ch[x][which^1],f[ch[old][which]]=old; ch[x][which^1]=old,f[old]=x,f[x]=fold; pushup(old),pushup(x); } void splay(int x) { int u=x; while(!isRoot(u)) u=f[u]; u=f[u]; for(int fa;(fa=f[x])!=u;rotate(x)) if(f[fa]!=u) rotate(get(fa)==get(x)?fa:x); } void Access(int x) { for(int y=0;x;y=x,x=f[x]) { splay(x); if(rson) { tmp[x][0][0]+=max(t[rson][0][0],t[rson][1][0]); tmp[x][1][0]+=t[rson][0][0]; } if(y) { tmp[x][0][0]-=max(t[y][0][0],t[y][1][0]); tmp[x][1][0]-=t[y][0][0]; } tmp[x][0][1]=tmp[x][0][0]; rson=y,pushup(x); } } }; //variables int DP[maxn][2]; int V[maxn],hd[maxn],to[maxn<<1],nex[maxn<<1]; int n,Q,edges; void add(int u,int v){ nex[++edges]=hd[u],hd[u]=edges,to[edges]=v; } //build graph void dfs(int u,int ff) { LCT::f[u]=ff; DP[u][0]=0; DP[u][1]=V[u]; for(int i=hd[u];i;i=nex[i]) { int v=to[i]; if(v==ff) continue; dfs(v,u); DP[u][0]+=max(DP[v][1],DP[v][0]); DP[u][1]+=DP[v][0]; } LCT::tmp[u]=(LCT::Matrix){ DP[u][0], DP[u][0], DP[u][1], -inf}; LCT::t[u]=LCT::tmp[u]; } //主程序~ int main() { // setIO("input"); scanf("%d%d",&n,&Q); for(int i=1;i<=n;++i) scanf("%d",&V[i]); for(int i=1,u,v;i<n;++i) { scanf("%d%d",&u,&v); add(u,v),add(v,u); } dfs(1,0); while(Q--) { int x,y; scanf("%d%d",&x,&y); LCT::Access(x); LCT::splay(x); LCT::tmp[x][1][0]+=(ll)y-V[x]; V[x]=y; LCT::pushup(x); LCT::splay(1); printf("%lld\n",max(LCT::t[1][0][0], LCT::t[1][1][0])); } return 0; }