看懂了后发现 $\texttt{DDP}$ 其实不难呢……
其实主要思想就是将 $\texttt{DP}$ 转移式搞到矩阵上,然后如果是树形 $\texttt{DP}$ 的话就可以直接上树剖或者是 $LCT$ 进行维护,当然还可以用全局平衡二叉树 (不费) 。如果只是线性的话可以直接用线段树等数据结构进行维护了。
注意这道模板树剖的复杂度是 $O(nlog^2n)$ ,而 $LCT$ 的复杂度为 $O(nlogn)$ ,于是窝选择了 $LCT$ ,跑的还挺快。
开始分析题目,如果没有” 动态” 限制的话就是一个裸的” 没有上司的舞会”,解法显然是设 $f[u][0/1]$ 表示 $u$ 不选/选 的时候其子树的最大价值,转移显然为:
$$f[u][0]=\sum \max(f[v][0],f[v][1])\\f[u][1]=val[u]+\sum f[v][0]$$
对于树中的一个节点 $u$ 的所有儿子中有个重儿子,其他的儿子就是轻儿子,我们将重儿子和轻儿子的贡献分开算。设一个 $g[u][0/1]$ ,其值为:
$$g[u][0]=\sum\max(f[v][0],f[v][1])\\g[u][1]=val[u]+\sum f[v][0]$$
注意上式中的 $v$ 只的是轻儿子,然后 $f$ 的转移就变成了以下形式 ( $x$ 为重儿子):
$$f[u][0]=\max(f[x][0],f[x][1])+g[u][0]\\f[u][1]=g[u][1]+f[x][0]$$
其实这里的 $g$ 很好维护,我们在 $Access$ 的时候只要计算儿子变化时的贡献就好了。
接着我们构造出转移矩阵:
$$
\begin{bmatrix}g[u][0] & g[u][0]\\g[u][1] & -inf\end{bmatrix}\cdot\begin{bmatrix}f[x][0] \\f[x][1]\end{bmatrix}=\begin{bmatrix}f[u][0]\\f[u][1]\end{bmatrix}
$$
这样子就可以直接更新了,对于每个节点我们只需要维护两个矩阵即可,一个就是上面乘法中的 $g$ 矩阵,一个就是上面乘法中的 $f$ 矩阵。
需要注意的是这是广义矩阵乘法,也就是说这个矩阵乘法的运算规则为:
$$c[i][j]=max(c[i][j],a[i][k]+b[k][j])$$
很像 $floyd$ ,可以直接算了。
Code:
#include <cstdio>
#include <string>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
const int N=1e5+2;
const int inf=1e9+9;
template <typename _Tp> inline void IN(_Tp&x) {
char ch;bool flag=0;x=0;
while(ch=getchar(),!isdigit(ch)) if(ch=='-') flag=1;
while(isdigit(ch)) x=x*10+ch-'0',ch=getchar();
if(flag) x=-x;
}
struct matrix {int c[2][2];matrix(){c[0][0]=c[0][1]=c[1][0]=c[1][1]=-inf;}};
matrix operator * (matrix&a,matrix&b) {
matrix ret;
for(int i=0;i<2;++i)for(int j=0;j<2;++j)for(int k=0;k<2;++k)
ret.c[i][j]=max(ret.c[i][j],a.c[i][k]+b.c[k][j]);
return ret;
}
int v[N],dp[N][2],head[N],nxt[N<<1],to[N<<1],cnt;
void add(int u,int v) {nxt[++cnt]=head[u],to[cnt]=v,head[u]=cnt;}
struct link_cut_tree {
matrix f[N],g[N];
int ch[N][2],fa[N];
inline bool isroot(int x) {return !((ch[fa[x]][0]==x)||(ch[fa[x]][1]==x));}
inline void pushup(int x) {
f[x]=g[x];
if(ch[x][0]) f[x]=f[ch[x][0]]*f[x];if(ch[x][1]) f[x]=f[x]*f[ch[x][1]];
}
inline void rotate(int x) {
int y=fa[x],z=fa[y],k=ch[y][1]==x,v=ch[x][!k];
if(!isroot(y)) ch[z][ch[z][1]==y]=x;ch[x][!k]=y,ch[y][k]=v;
if(v) fa[v]=y;fa[y]=x,fa[x]=z;pushup(y);
}
inline void Splay(int x) {
while(!isroot(x)) {
if(!isroot(fa[x]))
rotate((ch[fa[x]][0]==x)^(ch[fa[fa[x]]][0]==fa[x])?x:fa[x]);
rotate(x);
}pushup(x);return;
}
inline void Access(int x) {
for(int y=0;x;x=fa[y=x]) {
Splay(x);
if(ch[x][1]) {
g[x].c[0][0]+=max(f[ch[x][1]].c[0][0],f[ch[x][1]].c[1][0]);
g[x].c[1][0]+=f[ch[x][1]].c[0][0];
}
if(y) {
g[x].c[0][0]-=max(f[y].c[0][0],f[y].c[1][0]);
g[x].c[1][0]-=f[y].c[0][0];
}
g[x].c[0][1]=g[x].c[0][0];
ch[x][1]=y,pushup(x);
}return;
}
inline void change(int x,int y) {
Access(x),Splay(x),g[x].c[1][0]-=v[x]-y;
pushup(x),v[x]=y;return;
}
inline void build(int u) {
dp[u][1]=v[u];
for(int i=head[u];i;i=nxt[i]) {
int v=to[i];if(v!=fa[u]) {
fa[v]=u,build(v);
dp[u][0]+=max(dp[v][0],dp[v][1]);
dp[u][1]+=dp[v][0];
}
}
g[u].c[0][0]=g[u].c[0][1]=dp[u][0];
g[u].c[1][0]=dp[u][1];f[u]=g[u];
}
}T;
int main() {
int n,m;IN(n),IN(m);
for(int i=1;i<=n;++i) IN(v[i]);
for(int i=1,u,v;i<n;++i)IN(u),IN(v),add(u,v),add(v,u);
T.build(1);
while(m--) {
int x,y;IN(x),IN(y);
T.change(x,y),T.Splay(1);
printf("%d\n",max(T.f[1].c[0][0],T.f[1].c[1][0]));
}
return 0;
}
0 条评论