学习笔记第二十九节:动态Dp

版权声明:因为我是蒟蒻,所以请大佬和神犇们不要转载(有坑)的文章,并指出问题,谢谢 https://blog.csdn.net/Deep_Kevin/article/details/84175773

正题

      因为NOIP2018考了这一个东西,所以不得不学。

      我们以这一题为例题来引入今天的学习:【模板】动态dp

      我们显然可以用树形Dp去做,倒不如我们先把方程列出来。

      \\f[x][0]+=\sum_{i\in son[x]}max(f[i][1],f[i][0]); \\f[x][1]+=\sum_{i\in son[x]} f[i][0] ;

      这两条公式挺显然的吧。

      假设我们现在无聊,往树链剖分的角度去考虑。

      那么我们设一个数组g,表示的是从非重儿子转移来的状态,跟f数组的转移方程类似。

      我们可以把上面两条公式改写成什么呢?

      \\f[x][0]=g[x][0]+max(f[h_x][0],f[h_x][1]); \\f[x][1]=g[x][1]+f[h_x][0];

      这两条也是挺显然的吧。

      那么这个东西有什么用呢?

扫描二维码关注公众号,回复: 4358907 查看本文章

      对于一条重链来说,g肯定是不会改变的,但是f是可能改变的。

      我们试着去构造一个转移矩阵:

      \begin{vmatrix} f[h_x][0]\\f[h_x][1] \end{vmatrix}*\begin{vmatrix} g[x][0] & g[x][0]\\ g[x][1] & -\infty \end{vmatrix}=\begin{vmatrix} f[x][0]\\f[x][1] \end{vmatrix}

      我们定义这个乘法的含义是C=A*B,那么C_{i,j}=\max_k(A_{i,k}+B_{k,j})

      然后化简出来出来就是上面那两条公式。

      这个东西有什么用?

      容易发现\begin{vmatrix} f[x][0]\\f[x][1] \end{vmatrix}等于x所在重链底端到x的矩阵连乘积。这个矩阵指的是中间的哪一个矩阵,也就是对于x所在重链的底端到x的任一点p,\begin{vmatrix} f[x][0]\\f[x][1] \end{vmatrix}=\prod_p \begin{vmatrix} g[p][0] & g[p][0]\\ g[p][1] & -\infty \end{vmatrix}

      这个也是挺显然的吧,因为每一个点存的都是非重儿子的答案,那么多个非重儿子的答案合并就变成了该子树的答案。

      那么我们把一个点的权值变成了所在重链的乘积。(注意,上面的乘积和pi,都是新定义的*)

      假设我们要改x点的权值,那么x所在的矩阵要修改吧,那么就变成了改点。

      我们还可以求出不在这条重链上的第一个祖先,那么这个祖先的矩阵也要改,具体怎么改,可以通过原来x点所在重链的top的子树的答案的变化量来修改。

      一直这样往上跳,一共会改log个节点,因为要访问区间连乘积,所以用线段树维护,时间复杂度就是:O(n(log_2n )^2*8)

      虽然树链剖分的常数不大,但是这个log平方的算法已经够卡常的了。

      我还没有学会全局平衡二叉树。

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#define lc (now<<1)
#define rc ((now<<1)|1)
using namespace std;

int n,m;
int w[100010];
struct edge{
    int y,next;
}s[200010];
struct matrix{
    int op[3][3];
    matrix operator*(const matrix b)const{
        matrix q;
        for(int i=1;i<=2;i++)
            for(int j=1;j<=2;j++){
                q.op[i][j]=-1e9;
                for(int k=1;k<=2;k++)
                    q.op[i][j]=max(q.op[i][j],op[i][k]+b.op[k][j]);
            }
        return q;
    }
}tr[300010];
int first[100010],len=0;
int tot[100010],son[100010];
int image[100010],fact[100010],last[100010],fa[100010],top[100010];
int f[100010][2],g[100010][2];

void ins(int x,int y){
    len++;
    s[len]=(edge){y,first[x]};first[x]=len;
}

void dfs_1(int x){
    tot[x]=1;
    for(int i=first[x];i!=0;i=s[i].next){
        int y=s[i].y;
        if(y==fa[x]) continue;
        fa[y]=x;
        dfs_1(y);
        tot[x]+=tot[y];
        if(tot[y]>tot[son[x]]) son[x]=y;
    }
}

void dfs_2(int x,int tp){
    image[x]=++len;fact[len]=x;top[x]=tp;
    if(son[x]!=0) dfs_2(son[x],tp);
    else {last[tp]=x;return ;}
    for(int i=first[x];i!=0;i=s[i].next){
        if(s[i].y==fa[x] || s[i].y==son[x]) continue;
        dfs_2(s[i].y,s[i].y);
    }
}

void tr_dp(int x){
    f[x][1]=g[x][1]=w[x];
    f[x][0]=g[x][0]=0;
    for(int i=first[x];i!=0;i=s[i].next){
        int y=s[i].y;
        if(y==fa[x]) continue;
        tr_dp(y);
        f[x][0]+=max(f[y][0],f[y][1]);
        f[x][1]+=f[y][0];
        if(y==son[x]) continue;
        g[x][0]+=max(f[y][0],f[y][1]);
        g[x][1]+=f[y][0];
    }
}

void update(int now,int l,int r,int x){
    if(l==r){
        tr[now].op[1][1]=tr[now].op[1][2]=g[fact[x]][0];
        tr[now].op[2][1]=g[fact[x]][1];tr[now].op[2][2]=-1e9;
        return ;
    }
    int mid=(l+r)/2;
    if(x<=mid) update(lc,l,mid,x);
    else update(rc,mid+1,r,x);
    tr[now]=tr[lc]*tr[rc];
}

matrix query(int now,int x,int y,int l,int r){
    if(x==l && y==r) return tr[now];
    int mid=(l+r)/2;
    if(y<=mid) return query(lc,x,y,l,mid);
    else if(mid<x) return query(rc,x,y,mid+1,r);
    else return query(lc,x,mid,l,mid)*query(rc,mid+1,y,mid+1,r);
}

void build(int now,int l,int r){
    if(l==r){
        tr[now].op[1][1]=tr[now].op[1][2]=g[fact[l]][0];
        tr[now].op[2][1]=g[fact[l]][1];tr[now].op[2][2]=-1e9;
        return ;
    }
    int mid=(l+r)/2;
    build(lc,l,mid);build(rc,mid+1,r);
    tr[now]=tr[lc]*tr[rc];
}

void change(int x,int y){
    g[x][1]+=y-w[x];
    w[x]=y;
    matrix A,B;
    int st,ed;
    while(1){
        st=top[x];ed=last[top[x]];
        A=query(1,image[st],image[ed],1,n);
        update(1,1,n,image[x]);
        B=query(1,image[st],image[ed],1,n);
        x=fa[st];
        if(x==0) break;
        g[x][0]+=max(B.op[1][1],B.op[2][1])-max(A.op[1][1],A.op[2][1]);
        g[x][1]+=B.op[1][1]-A.op[1][1];
    }
}

int main(){
    scanf("%d %d",&n,&m);
    for(int i=1;i<=n;i++) scanf("%d",&w[i]);
    int x,y;
    for(int i=1;i<=n-1;i++) {
        scanf("%d %d",&x,&y);
        ins(x,y);ins(y,x);
    }
    len=0;
    dfs_1(1);dfs_2(1,1);
    tr_dp(1);
    build(1,1,n);
    for(int i=1;i<=m;i++){
        scanf("%d %d",&x,&y);
        change(x,y);
        matrix A=query(1,image[1],image[last[1]],1,n);
        printf("%d\n",max(A.op[1][1],A.op[2][1]));
    }
}

猜你喜欢

转载自blog.csdn.net/Deep_Kevin/article/details/84175773