• P4719 【模板】动态dp


    传送门

    shadowice巨巨太强啦

    表示连树剖都不会直接来肝这个近乎LCT的很懵逼啊……于是一个晚上就过去了……

    首先,这题本质上就是个没有上司的舞会,然而带修改

    先考虑正常的dp方程,设(dp_{u,0})表示该点不选的最大收益,(dp_{u,1})表示该点选的最大收益,则有$$dp_{u,0}=sum max(dp_{v,0},dp_{v,1})$$

    [dp_{u,1}=val_u+sum dp_{v,0} ]

    我们考虑一下把整棵树给树剖,一条一条重链来求出dp值。每一次到一条重链的时候,先递归下去吧所有与它相连的其他重链给dp完,然后再来求出它的dp值。算这条重链的时候,先算出轻儿子的dp值,设为(ldp_{u,0/1}),则有$$ldp_{u,0}=sum_{vin u.lightson}max(dp_{v,0},dp_{v,1})$$

    [ldp_{u,1}=sum_{vin u.lightson}dp_{v,0} ]

    那么重链上的dp就可以放到一个序列上进行了

    [dp_{u,0}=ldp_{u,0}+max(dp_{u.heavyson,0},dp_{u.heavyson,1}) ]

    [dp_{u,1}=val_u+ldp_{1,u}+dp_{u.heavyson,0} ]

    于是我们现在就把一个树上的问题转化为了一个序列的问题,每一次单点修改的时候可以在线段树上对应的重链进行修改,那么会改变重链顶点的dp值,于是又要改变重链顶点父亲的dp值,这样不断搞上去,就可以用树剖做到(O(log^2n))

    然后关于怎么在线段树上维护dp值,据猫老师说,我们可以重定义一个矩乘,即$$C_{i,j}=max_k(A_{i,k}+B_{k,j})$$
    而且这个新的矩乘还是满足结合律的(不过不满足交换律),单位矩阵是主对角线上是(0),其他全都是(-infty),那么重链的转移可以写成矩乘的形式,那么就可以在线段树上维护矩阵连乘积了

    以上是树剖的做法,然后是shadowice巨巨说的“全局平衡二叉树”

    具体来说,就是把树剖换成LCT,再通过一些手段来减小LCT的常数,从而达到优化时间复杂度的目的

    首先这是棵静态树,建LCT没有必要,所以把LCT给静态化

    我们先对原树进行一遍dfs,然而在对重链建bst并不建一个严格的bst,在每一个节点设一个权值,为它所有轻儿子的(size)总和,然后选择带权重心作为这一级的父亲,递归往两边建bst

    然后发现在这棵树里面从底往上经过的不管是实边还是虚边,子树的(size)都会翻一倍,所以这个树的树高是(O(logn))

    然后记得注意矩乘的时候是从底下往上乘的,所以LCT里应该是深度大的在左子树,因为这个矩乘是不满足交换律的要很注意顺序

    于是这玩意儿就能做到(O(nlogn))的时间复杂度了

    //minamoto
    #include<bits/stdc++.h>
    #define R register
    #define inf 0x3f3f3f3f
    #define fp(i,a,b) for(R int i=a,I=b+1;i<I;++i)
    #define fd(i,a,b) for(R int i=a,I=b-1;i>I;--i)
    #define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
    using namespace std;
    char buf[1<<21],*p1=buf,*p2=buf;
    inline char getc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++;}
    int read(){
        R int res,f=1;R char ch;
        while((ch=getc())>'9'||ch<'0')(ch=='-')&&(f=-1);
        for(res=ch-'0';(ch=getc())>='0'&&ch<='9';res=res*10+ch-'0');
        return res*f;
    }
    char sr[1<<21],z[20];int C=-1,Z=0;
    inline void Ot(){fwrite(sr,1,C+1,stdout),C=-1;}
    void print(R int x){
        if(C>1<<20)Ot();if(x<0)sr[++C]='-',x=-x;
        while(z[++Z]=x%10+48,x/=10);
        while(sr[++C]=z[Z],--Z);sr[++C]='
    ';
    }
    inline int min(R int x,R int y){return x<y?x:y;}
    inline int max(R int x,R int y){return x>y?x:y;}
    const int N=1e5+5;
    struct eg{int v,nx;}e[N<<1];int head[N],tot;
    inline void add(R int u,R int v){e[++tot]={v,head[u]},head[u]=tot;}
    int sz[N],son[N],val[N],n,m;
    void dfs(int u,int fa){
        sz[u]=1;go(u)if(v!=fa){
            dfs(v,u),sz[u]+=sz[v];
            if(sz[v]>sz[son[u]])son[u]=v;
        }
    }
    struct Matrix{
        int a[2][2];
        Matrix(){a[0][0]=a[0][1]=a[1][0]=a[1][1]=-inf;}
        Matrix(R int x){a[0][0]=a[1][1]=0,a[0][1]=a[1][0]=-inf;}
        inline int mx(){return max(max(a[0][0],a[0][1]),max(a[1][0],a[1][1]));}
        inline int* operator [](R int x){return a[x];}
        Matrix operator *(Matrix b){
            Matrix res;
            res[0][0]=max(a[0][0]+b[0][0],a[0][1]+b[1][0]);
            res[0][1]=max(a[0][0]+b[0][1],a[0][1]+b[1][1]);
            res[1][0]=max(a[1][0]+b[0][0],a[1][1]+b[1][0]);
            res[1][1]=max(a[1][0]+b[0][1],a[1][1]+b[1][1]);
            return res;
        }
    }mul[N],w[N];int ch[N][2],fa[N],st[N],lsz[N],top,rt;bool vis[N];
    inline void upd(R int x){mul[x]=mul[ch[x][0]]*w[x]*mul[ch[x][1]];}
    inline void pd(R int x,R int v){
        w[x][1][0]+=mul[v].mx(),w[x][0][0]=w[x][1][0],w[x][0][1]+=mul[v][0][0],fa[v]=x;
    }
    inline void init(){fp(i,1,n)w[i][0][1]=val[i],w[i][0][0]=w[i][1][0]=0;}
    inline bool is(R int x){return ch[fa[x]][0]!=x&&ch[fa[x]][1]!=x;}
    int sbuild(int l,int r){
        if(l>r)return 0;int tot=0;for(int i=l;i<=r;++i)tot+=lsz[st[i]];
        for(int i=l,now=lsz[st[i]];i<=r;++i,now+=lsz[st[i]])
        if(2*now>=tot){
            ch[st[i]][0]=sbuild(i+1,r),ch[st[i]][1]=sbuild(l,i-1);
            fa[ch[st[i]][0]]=fa[ch[st[i]][1]]=st[i],upd(st[i]);return st[i];
        }
    }
    int build(int x){
        for(int p=x;p;p=son[p])vis[p]=1;
        for(int p=x;p;p=son[p])go(p)if(!vis[v])pd(p,build(v));
        top=0;for(int p=x;p;p=son[p])st[++top]=p;
        for(int p=x;p;p=son[p])lsz[p]=sz[p]-sz[son[p]];return sbuild(1,top);
    }
    void update(int x,int vva){
        w[x][0][1]+=vva-val[x],val[x]=vva;
        for(int p=x;p;p=fa[p])if(is(p)&&fa[p]){
            w[fa[p]][0][0]-=mul[p].mx(),w[fa[p]][1][0]=w[fa[p]][0][0];
            w[fa[p]][0][1]-=mul[p][0][0],upd(p);
            w[fa[p]][0][0]+=mul[p].mx(),w[fa[p]][1][0]=w[fa[p]][0][0];
            w[fa[p]][0][1]+=mul[p][0][0];
        }else upd(p);
    }
    int main(){
    //	freopen("testdata.in","r",stdin);
        w[0]=mul[0]=Matrix(1),n=read(),m=read();
        fp(i,1,n)val[i]=read();
        for(R int i=1,u,v;i<n;++i)u=read(),v=read(),add(u,v),add(v,u);
        dfs(1,0),init(),rt=build(1);int p,w;
        while(m--)p=read(),w=read(),update(p,w),print(mul[rt].mx());
        return Ot(),0;
    }
    
  • 相关阅读:
    AndroidStudio项目CMakeLists解析
    Xposed那些事儿 — xposed框架的检测和反制
    从谷歌官网下载android 6.0源码、编译并刷入nexus 6p手机
    编译Xposed
    常用的delphi 第三方控件
    delphi安装控件
    delphi控件安装与删除
    从今天开始,每天都要写博客,加油
    关于ArrayAdapter的getCount()的方法会造成空指针异常的分析
    实现ListView的加载更多的效果,如何将按钮布局到始终在ListView的最后一行
  • 原文地址:https://www.cnblogs.com/bztMinamoto/p/10167946.html
Copyright © 2020-2023  润新知