学习笔记第二十九节:动态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} g[x][0] & g[x][0]\\ g[x][1] & -\infty \end{vmatrix}\begin{vmatrix} f[h_x][0]\\f[h_x][1] \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
#include
#include
#include
#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

 

你可能感兴趣的:(学习笔记)