zoukankan      html  css  js  c++  java
  • 动态dp

    动态dp是一个毒瘤且奇葩的东西...

    然而noip2018出了这个东西...

    因此...

    一道题为例吧:给出一棵带点权的树,每次修改一个点的点权,查询这棵树的最大权独立集(可以理解为每次询问一遍“没有上司的舞会”)

    首先考虑暴力:

    设状态$f[i][0/1]$表示以$i$为根的子树,点$i$选或不选所能获得的最大权独立集权值,设$i$点权值为$w_{i}$,显然有转移:

    $f[i][0]=sum max(f[son][0],f[son][1])$

    $f[i][1]=w_{i}+sum f[son][0]$

    显然这样转移每次都是$O(n)$的,复杂度爆炸

    考虑每次修改会影响的范围:显然,每次修改只会影响从这个点到根节点的一条树链,那么我们考虑是否可以用树链剖分来进行优化

    树链剖分显然需要套个数据结构维护,考虑线段树

    (这里用LCT+SPLAY好像也可以,但是我不想写...)

    怎么维护?

    线段树支持单点修改,区间查询,但是...查什么呢?

    看到这是一个递推的形式,考虑线段树维护转移矩阵

    喂,这玩意怎么写转移矩阵嘛!

    考虑借助一下东方的神秘力量其他变量

    记$g[i][0]$表示以$i$为根的子树中所有非重儿子任意选取的价值之和,即$g[i][0]=f[i][0]-max(f[son_{i}][0],f[son_{i}][1])$

    记$g[i][1]$表示以$i$为根的子树中所有非重儿子必须不选的价值与该节点权值之和,即$g[i][1]=f[i][1]-f[son_{i}][0]$

    ($son_{i}$表示$i$的重儿子,$g$也就是$f$去掉了重儿子的贡献)

    给出这两个定义之后就可以重写转移方程了:

    $f[i][0]=g[i][0]+max(f[son_{i}][0],f[son_{i}][1])$

    $f[i][1]=g[i][1]+f[son_{i}][0]$

    这么倒腾半天有啥用?

    把两个转移方程都写成直接取最大值的形式,就是:

    $f[i][0]=max(g[i][0]+f[son_{i}][0],g[i][0]+f[son_{i}][1])$

    $f[i][1]=max(g[i][1]+f[son_{i}][0],-infty)$

    (后面那个$infty$怕不是打酱油的...)

    这又有啥用?能变成矩阵吗?

    好像不能,但是...如果我们重新定义矩阵乘法的运算规则呢?

    定义矩阵乘法$A*B=C$,运算规则如下:

    $C_{ij}=(max_{k=1}^{n}A_{ik}+B_{kj})$(表示对后面那一堆取最大值)

    这又有啥用?

    这样就可以把转移写成矩阵形式了!

    $egin{pmatrix}f[i][0] \f[i][1]end{pmatrix}$ $=$ $egin{pmatrix} g[i][0] & g[i][0] \ g[i][1 ]& -infty end{pmatrix}$ $egin{pmatrix} f[son_{i}][0]\f[son_{i}][1] end{pmatrix}$

    那么如果这个东西满足结合律,就可以直接上线段树维护了

    瞎**YY一下,这个东西满足结合律!

    那么上线段树维护即可

    考虑怎么维护:

    每修改一个点时,我们事实上修改的是这个点对应的转移矩阵中的$g[i][1]$!

    因此直接修改即可

    再考虑修改一个点对上面的点的影响:

    首先对这条重链上的点没有影响,因为$g$记录的都是轻儿子的状态

    然后会对这条重链链顶的父亲有影响,因此每次跳到链顶的父亲即可(注意到重链的链顶一定是自己父亲的轻儿子)

    考虑有什么影响:修改一个点会影响这个点所对应的链顶的$f$值,而计算一个链顶的$f$其实就是把这条重链上每个点维护的转移矩阵乘起来!

    因此我们在修改时计算修改前后这个链顶的$f$的差值,也就算出了链顶对其父节点的影响了

    这样向上跳跃更新即可

    时间复杂度$O(nlog_{2}^{2}n)$

    贴代码:

    #include <cstdio>
    #include <cmath>
    #include <cstring>
    #include <cstdlib>
    #include <iostream>
    #include <algorithm>
    #include <vector>
    #define rt1 rt<<1
    #define rt2 (rt<<1)|1
    #define ll long long
    using namespace std;
    const ll inf=0x3f3f3f3f3f3f3f3fll;
    struct MAT
    {
        ll a[2][2];
        friend MAT operator * (MAT x,MAT y)
        {
            MAT ret;
            ret.a[0][0]=max(x.a[0][0]+y.a[0][0],x.a[0][1]+y.a[1][0]);
            ret.a[0][1]=max(x.a[0][1]+y.a[1][1],x.a[0][0]+y.a[0][1]);
            ret.a[1][0]=max(x.a[1][0]+y.a[0][0],x.a[1][1]+y.a[1][0]);
            ret.a[1][1]=max(x.a[1][1]+y.a[1][1],x.a[1][0]+y.a[0][1]);
            return ret;
        }
    }ori[100005];
    MAT tree[400005];
    vector <int> v[100005];
    int ttop[100005],dep[100005],f[100005],siz[100005],son[100005],ed[100005];
    int nnum[100005],onum[100005];
    int tot=0;
    ll w[100005],F[100005][2];
    int n,m;
    void dfs(int x,int fx)
    {
        siz[x]=1,dep[x]=dep[fx]+1,f[x]=fx;
        for(int i=0;i<v[x].size();i++)
        {
            int to=v[x][i];
            if(to==fx)continue;
            dfs(to,x);
            siz[x]+=siz[to];
            if(siz[son[x]]<siz[to])son[x]=to;
        }
    }
    void redfs(int x,int fx,int topx)
    {
        ttop[x]=topx,nnum[x]=++tot,onum[tot]=x;
        if(son[x])redfs(son[x],x,topx),ed[x]=ed[son[x]];
        else ed[x]=x;
        for(int i=0;i<v[x].size();i++)
        {
            int to=v[x][i];
            if(to==fx||to==son[x])continue;
            redfs(to,x,to);
        }
    }
    void tree_dp(int x,int fx)
    {
        F[x][1]=w[x];
        for(int i=0;i<v[x].size();i++)
        {
            int to=v[x][i];
            if(to==fx)continue;
            tree_dp(to,x);
            F[x][1]+=F[to][0],F[x][0]+=max(F[to][0],F[to][1]);
        }
    }
    void buildtree(int rt,int l,int r)
    {
        if(l==r)
        {
            int p=onum[l];
            ll g0=F[p][0]-max(F[son[p]][1],F[son[p]][0]),g1=F[p][1]-F[son[p]][0];
            ori[p].a[0][0]=ori[p].a[0][1]=g0;
            ori[p].a[1][0]=g1,ori[p].a[1][1]=-inf;
            tree[rt]=ori[p];
            return;
        }
        int mid=(l+r)>>1;
        buildtree(rt1,l,mid),buildtree(rt2,mid+1,r);
        tree[rt]=tree[rt1]*tree[rt2];
    }
    void update(int rt,int l,int r,int posi)
    {
        if(l==r){tree[rt]=ori[onum[l]];return;}
        int mid=(l+r)>>1;
        if(posi<=mid)update(rt1,l,mid,posi);
        else update(rt2,mid+1,r,posi);
        tree[rt]=tree[rt1]*tree[rt2];
    }
    MAT query(int rt,int l,int r,int lq,int rq)
    {
        if(l>=lq&&r<=rq)return tree[rt];
        int mid=(l+r)>>1;
        if(rq<=mid)return query(rt1,l,mid,lq,rq);
        else if(lq>mid)return query(rt2,mid+1,r,lq,rq);
        else return query(rt1,l,mid,lq,rq)*query(rt2,mid+1,r,lq,rq);
    }
    void ins(int p,ll t)
    {
        ori[p].a[1][0]+=t-w[p],w[p]=t;
        while(p)
        {
            MAT m0=query(1,1,n,nnum[ttop[p]],nnum[ed[p]]);
            update(1,1,n,nnum[p]);
            MAT m1=query(1,1,n,nnum[ttop[p]],nnum[ed[p]]);
            p=f[ttop[p]];
            if(!p)break;
            ori[p].a[0][0]+=max(m1.a[0][0],m1.a[1][0])-max(m0.a[0][0],m0.a[1][0]);
            ori[p].a[0][1]=ori[p].a[0][0];
            ori[p].a[1][0]+=m1.a[0][0]-m0.a[0][0];
        }
    }
    template <typename T>inline void read(T &x)
    {
        T f=1,c=0;char ch=getchar();
        while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
        while(ch>='0'&&ch<='9'){c=c*10+ch-'0';ch=getchar();}
        x=c*f;
    }
    int main()
    {
        read(n),read(m);
        for(int i=1;i<=n;i++)read(w[i]);
        for(int i=1;i<n;i++)
        {
            int x,y;
            read(x),read(y);
            v[x].push_back(y),v[y].push_back(x);
        }
        dfs(1,0),redfs(1,0,1),tree_dp(1,1),buildtree(1,1,n);
        while(m--)
        {
            int p;ll t;
            read(p),read(t);
            ins(p,t);
            MAT ret=query(1,1,n,nnum[1],nnum[ed[1]]);
            printf("%lld
    ",max(ret.a[0][0],ret.a[1][0]));
        }
        return 0;
    }
  • 相关阅读:
    mybatis中_parameter使用和常用sql
    ibatis中井号跟美元符号区别(#.$)
    mybatis动态sql中的trim标签的使用
    c语言捕捉异常
    lua lua解读
    lua luaconf解读
    android堆栈调试--详细
    cocos2d-x安卓应用启动调用过程简析
    ndk-stack使用方法
    cocos2dx3.2移植android
  • 原文地址:https://www.cnblogs.com/zhangleo/p/11091300.html
Copyright © 2011-2022 走看看