看懂了后发现 $\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;
}
最后修改:2019 年 05 月 30 日 03 : 23 PM