复习动规(2)

今天早上换寝室,耽误了一些时间。还是继续复习动态规划。

单调队列优化DP

第一道,宝物筛选一道多重背包优化题。如果用二进制优化很好做,但时间复杂度是O(nW*logm)。单调队列优化做法如下:

首先做出普通的多重背包的转移方程:f[j]=max{f[j-w*k]+v*k},w为重量,v为价值。

使得j=aw+b,即a=j/w,b=j%w。原方程可转化为f[j]=max{f[(a-k)*w+b]-(a-k)*v}+a*v。

这个时候我们就可以枚举b,将a-k看做整体,对于每个b都对f[(a-k)*w+b]-(a-k)*v做单调队列,再将a从大到小循环就可以了。

方程的转化比较难想到,代码的具体实现如下:

#include<bits/stdc++.h>
using namespace std;
const int N=4e4+1e3;
int n,W,p[N];
long long f[N],q[N];
long long Max(long long x,long long y){
    return x>y?x:y;
}
int Min(int x,int y){
    return x<y?x:y;
}
int rd(){
    int s=0,ff=1;
    char w=getchar();
    while(!isdigit(w))
        ff^=w=='-',w=getchar();
    while(isdigit(w))
        s=s*10+w-'0',w=getchar();
    return ff?s:-s;
}
int main(){
    int v,w,m,l,r,a; long long x;
    n=rd(); W=rd();
    for(int i=1;i<=n;i++){
        v=rd(),w=rd(),m=rd();
        for(int b=0;b<w;b++){
            l=1,r=0,a=(W-(W%w-b+w)%w)/w;
            for(int k=1;k<=Min(a,m);k++){
                x=f[(a-k)*w+b]-(a-k)*v;
                while(l<=r&&q[r]<=x) r--;
                q[++r]=x,p[r]=a-k;
            }
            for(int j=W-(W%w-b+w)%w;j!=b;j-=w){
                a=j/w; while(p[l]>=a) l++;
                f[j]=Max(f[j],q[l]+a*v);
                if(a>=m+1) x=f[(a-m-1)*w+b]-(a-m-1)*v;
                while(l<=r&&q[r]<=x) r--;
                q[++r]=x,p[r]=a-m-1;
            }
        }
    }
    printf("%lld
",f[W]);
    return 0;
}
宝物筛选

数据结构优化DP

第一道,"动态 DP"&动态树分治剩下的大多数时间都花在上面了,头疼。

动态DP具体的思想和做法看这篇博客吧,个人觉得写的非常好。

我在这里讲下操作的具体实现(为了方便表达,把新定义的符号叫做乘号):

对于点x,它的权值要改为y,首先它这个点的矩阵要先修改(别忘了权值要改)。(注意这个点的矩阵和线段树上的矩阵是两个矩阵

然后进入while循环,先求出Top[x](Top[x]为x所在重链的顶点)的f值,求法是对x这一整条重链即线段树上的连续一段求矩阵的连乘,因为叶子节点的f值等于g值,所以乘出来后矩阵第1行第1列就是f[Top[x]][0],第2行第1列就是f[Top[x]][1]。

再用x这个点的矩阵更新线段树上x的矩阵,之后再求出Top[x]的f值。

再就可以用Top[x]的新旧f值修改树上“Top[x]的父亲”这个点的矩阵(具体实现看代码,好理解的)。然后x=“Top[x]的父亲”。

然后while循环结束了,不用担心Top[x]的父亲也就是现在的x没有在线段树上更新,因为在下个循环会更新到的。

答案就是求出1的f值,取最大值输出即可。代码具体实现如下:

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+1e4;
int n,m,cnt,tot,h[N],d[N],f[N][2],g[N][2],fa[N],dep[N],siz[N],Son[N],Top[N],dfn[N],sfn[N],End[N];
int Max(int x,int y){
    return x>y?x:y;
}
struct nod{
    int nxt,to;
}a[N*2];
struct Num{
    int p[3][3];
    Num(){
        memset(p,0,sizeof(p));
        p[2][2]=-1e8;
    }
    inline Num operator*(Num b){
        Num c;
        for(int i=1;i<=2;i++)
            for(int j=1;j<=2;j++)
                for(int k=1;k<=2;k++)
                    c.p[i][j]=Max(c.p[i][j],p[i][k]+b.p[k][j]);
        return c;
    }
}b[N*4],c[N],ans;
int rd(){
    int s=0,ff=1;
    char w=getchar();
    while(!isdigit(w))
        ff^=w=='-',w=getchar();
    while(isdigit(w))
        s=s*10+w-'0',w=getchar();
    return ff?s:-s;
}
void Add(int x,int y){
    a[++cnt].nxt=h[x];
    a[cnt].to=y;
    h[x]=cnt;
}
void Dfs(int x,int w){ int y; siz[x]=1;
    for(int i=h[x];i;i=a[i].nxt){
        y=a[i].to;
        if(y==w) continue ; fa[y]=x;
        dep[y]=dep[x]+1; Dfs(y,x);
        siz[x]+=siz[y];
        if(siz[y]>siz[Son[x]])
            Son[x]=y;
    }
}
void Dfss(int x,int w){ int y;
    dfn[++tot]=x; sfn[x]=tot;
    if(Son[x])
        Top[Son[x]]=Top[x],Dfss(Son[x],x),End[x]=End[Son[x]];
    else End[x]=x;
    for(int i=h[x];i;i=a[i].nxt){
        y=a[i].to;
        if(y==w||y==Son[x]) continue ;
        Top[y]=y; Dfss(y,x);
    }
    
}
void Dfs_dp(int x,int w){ int y;
    f[x][0]=g[x][0]=0; f[x][1]=g[x][1]=d[x];
    for(int i=h[x];i;i=a[i].nxt){
        y=a[i].to; if(y==w) continue ; Dfs_dp(y,x);
        f[x][0]+=Max(f[y][0],f[y][1]),f[x][1]+=f[y][0];
        if(y!=Son[x])
            g[x][0]+=Max(f[y][0],f[y][1]),g[x][1]+=f[y][0];
    }
}
void Build(int x,int l,int r){
    if(l==r){
        b[x].p[1][1]=g[dfn[l]][0];
        b[x].p[1][2]=g[dfn[l]][0];
        b[x].p[2][1]=g[dfn[l]][1];
        b[x].p[2][2]=-1e8;
        c[dfn[l]]=b[x]; return ;
    }
    int mid=(l+r)>>1;
    Build(x<<1,l,mid);
    Build(x<<1|1,mid+1,r);
    b[x]=b[x<<1]*b[x<<1|1];
}
void Update(int x,int l,int r,int s){
    if(l==r){
        b[x]=c[dfn[l]]; return ;
    }
    int mid=(l+r)>>1;
    if(s<=mid) Update(x<<1,l,mid,s);
    else Update(x<<1|1,mid+1,r,s);
    b[x]=b[x<<1]*b[x<<1|1];
}
Num Query(int x,int l,int r,int L,int R){
    if(L<=l&&r<=R) return b[x];
    int mid=(l+r)>>1;
    if(mid>=R) return Query(x<<1,l,mid,L,R);
    else if(mid<L) return Query(x<<1|1,mid+1,r,L,R);
    else return Query(x<<1,l,mid,L,R)*Query(x<<1|1,mid+1,r,L,R);
}
int main(){
    int x,y,z; Num A,B;
    n=rd(); m=rd();
    for(int i=1;i<=n;i++)
        d[i]=rd();
    for(int i=1;i<n;i++)
        x=rd(),y=rd(),Add(x,y),Add(y,x);
    dep[1]=1; Dfs(1,0);
    Top[1]=1; Dfss(1,0);
    Dfs_dp(1,0); Build(1,1,n);
    while(m--){
        x=rd(),y=rd();
        c[x].p[2][1]+=y-d[x]; d[x]=y;
        while(x){
            A=Query(1,1,n,sfn[Top[x]],sfn[End[x]]);
            Update(1,1,n,sfn[x]);
            B=Query(1,1,n,sfn[Top[x]],sfn[End[x]]);
            x=fa[Top[x]];
            c[x].p[1][1]+=Max(B.p[1][1],B.p[2][1])-Max(A.p[1][1],A.p[2][1]);
            c[x].p[1][2]+=Max(B.p[1][1],B.p[2][1])-Max(A.p[1][1],A.p[2][1]);
            c[x].p[2][1]+=B.p[1][1]-A.p[1][1];
        }
        A=Query(1,1,n,sfn[1],sfn[End[1]]);
        printf("%d
",Max(A.p[1][1],A.p[2][1]));
    }
    return 0;
}
"动态DP"&动态树分治

今天就这样吧,看了几题比较简单就没写了,明天再说。

原文地址:https://www.cnblogs.com/manmanjiangQwQ/p/13298789.html