码迷,mamicode.com
首页 > 其他好文 > 详细

动态dp

时间:2019-06-26 19:24:39      阅读:104      评论:0      收藏:0      [点我收藏+]

标签:去掉   获得   cto   href   struct   math   形式   bsp   ack   

动态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})$(表示对后面那一堆取最大值)

这又有啥用?

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

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

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

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

那么上线段树维护即可

考虑怎么维护:

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

因此直接修改即可

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

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

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

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

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

这样向上跳跃更新即可

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

贴代码:

#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\n",max(ret.a[0][0],ret.a[1][0]));
    }
    return 0;
}

 

动态dp

标签:去掉   获得   cto   href   struct   math   形式   bsp   ack   

原文地址:https://www.cnblogs.com/zhangleo/p/11091300.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!