1. 程式人生 > >學習筆記第二十九節:動態Dp

學習筆記第二十九節:動態Dp

正題

      因為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];

      這兩條也是挺顯然的吧。

      那麼這個東西有什麼用呢?

      對於一條重鏈來說,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]));
    }
}