zoukankan      html  css  js  c++  java
  • [线段树优化dp] Codeforces 1304F2 Animal Observation (hard version)

    题目大意

    给定一个(N imes M(N leq 50,M leq 20000))的矩阵,给定(Kleq M),要求以每行的某个点为左上角选取一个(2 imes K)的子矩阵,使得所有选出的子矩阵覆盖的值之和最大,输出这个最大值。如下图。

    题解

    这道题的Easy版本和Hard版本唯一的区别就是(K)的大小不一样,Easy版本(K)是20,Hard版本(K)是20000。

    容易想到dp,设(dp[i][j])表示考虑了前(i)行,且以第(i)行的第(j)个格子为右下角选取了一个(2 imes K)的矩阵时的最大价值。

    那么(dp[i][j]=max(dp[i-1][k]-S))(S)表示本次选的以((i,j))为右下角的矩形和上一次选的以((i-1,k))为右下角的矩形的相交部分的价值,最终的答案就是(min{dp[N][j]})

    容易想到,对于两个矩形没有相交的情况,可以维护(dp[i][j])的前缀最大值和后缀最大值,然后状态转移是(O(1))的。

    对于两个矩形相交的情况,对于(Easy)版本,因为(K)只有20,我们可以去暴力维护。但对于Hard版本,复杂度太高。

    对于(dp[i][j]),我们是固定了(i),然后从从小到大枚举(j)(dp)的。考虑(dp[i][j-1])(dp[i][j])之间的关系。

    (dp[i][j-1])选取的是左上角为((i-1,j-K)),右下角为((i,j-1))的矩形,(dp[i][j])选取的是左上角为((i-1,j-K+1)),右下角为((i,j))的矩形,相当于选取的矩形往右移动了1格。那么对于右下角在第(i-1)([j-K,j-1])范围内的矩形,在当前选取的矩形往右移动一格时,其(dp)值要加上(Matrix[i-1][j-K]),对于右下角在第(i-1)([j,j+K-1])范围内的矩形,在当前选取的矩形往右移动一格时,其(dp)值要减去(Matrix[i-1][j])

    即要维护区间加、区间最大值,可以用线段树做。时间复杂度(O(NMlogM))

    Codeforces 1304F1 Easy版代码

    #include <iostream>
    #include <algorithm>
    #include <cstring>
    #include <cstdio>
    #include <cmath>
    using namespace std;
    
    #define RG register int
    #define LL long long
    
    template<typename elemType>
    inline void Read(elemType &T){
        elemType X=0,w=0; char ch=0;
        while(!isdigit(ch)) {w|=ch=='-';ch=getchar();}
        while(isdigit(ch)) X=(X<<3)+(X<<1)+(ch^48),ch=getchar();
        T=(w?-X:X);
    }
    
    int Matrix[53][20010],Sum[53][20010],dp[53][20010];
    int Pre[53][20010],Suf[53][20010];
    int N,M,K;
    
    inline int Calc(int x,int y){
        int Res=0;
        for(RG j=max(y-K+1,K);j<=y;++j){
            int temp=Sum[x][y]-Sum[x-2][y]-Sum[x][y-K]+Sum[x-2][y-K]
                    +dp[x-1][j]-(Sum[x-1][j]-Sum[x-1][y-K]-Sum[x-2][j]+Sum[x-2][y-K]);
            Res=max(Res,temp);
        }
        for(RG j=y+1;j<=min(M,y+K-1);++j){
            int temp=Sum[x][y]-Sum[x-2][y]-Sum[x][y-K]+Sum[x-2][y-K]
                    +dp[x-1][j]-(Sum[x-1][y]-Sum[x-1][j-K]-Sum[x-2][y]+Sum[x-2][j-K]);
            Res=max(Res,temp);
        }
        return Res;
    }
    
    int main(){
        Read(N);Read(M);Read(K);
        for(RG i=1;i<=N;++i)
            for(RG j=1;j<=M;++j)
                Read(Matrix[N-i+2][j]);
        for(RG i=1;i<=N+1;++i)
            for(RG j=1;j<=M;++j)
                Sum[i][j]=Sum[i-1][j]+Sum[i][j-1]-Sum[i-1][j-1]+Matrix[i][j];
        for(RG i=K;i<=M;++i)
            dp[2][i]=Sum[2][i]-Sum[0][i]-Sum[2][i-K]+Sum[0][i-K];
        for(RG j=1;j<=M;++j){
            Pre[2][j]=max(Pre[2][j-1],dp[2][j]);
            Suf[2][M-j+1]=max(Suf[2][M-j+2],dp[2][M-j+1]);
        }
        for(RG i=3;i<=N+1;++i){
            for(RG j=K;j<=M;++j){
                dp[i][j]=Pre[i-1][j-K]+Sum[i][j]-Sum[i-2][j]-Sum[i][j-K]+Sum[i-2][j-K];
                if(j+K<=M) dp[i][j]=max(dp[i][j],Suf[i-1][j+K]+Sum[i][j]-Sum[i-2][j]-Sum[i][j-K]+Sum[i-2][j-K]);
                dp[i][j]=max(dp[i][j],Calc(i,j));
            }
            for(RG j=1;j<=M;++j){
                Pre[i][j]=max(Pre[i][j-1],dp[i][j]);
                Suf[i][M-j+1]=max(Suf[i][M-j+2],dp[i][M-j+1]);
            }
        }
        int Ans=0;
        for(RG i=K;i<=M;++i)
            Ans=max(Ans,dp[N+1][i]);
        printf("%d
    ",Ans);
        return 0;
    }
    

    Codeforces 1304F2 Hard版代码

    #include <iostream>
    #include <algorithm>
    #include <cstring>
    #include <cstdio>
    #include <cmath>
    using namespace std;
    
    #define RG register int
    #define LL long long
    
    template<typename elemType>
    inline void Read(elemType &T){
        elemType X=0,w=0; char ch=0;
        while(!isdigit(ch)) {w|=ch=='-';ch=getchar();}
        while(isdigit(ch)) X=(X<<3)+(X<<1)+(ch^48),ch=getchar();
        T=(w?-X:X);
    }
    
    int Matrix[53][20010],Sum[53][20010],dp[53][20010];
    int Pre[53][20010],Suf[53][20010];
    int N,M,K;
    
    struct SegTreeNode{int Value,Lazytag;};
    SegTreeNode SegTree[80010];
    int Data[20010];
    
    void Build_SegTree(int Root,int L,int R){
        SegTree[Root].Lazytag=0;
        if(L==R){SegTree[Root].Value=Data[L];return;}
        int mid=(L+R)>>1;
        Build_SegTree(Root<<1,L,mid);
        Build_SegTree(Root<<1|1,mid+1,R);
        SegTree[Root].Value=max(SegTree[Root<<1].Value,SegTree[Root<<1|1].Value);
        return;
    }
    
    inline void Push_Down(int Root){
        if(!SegTree[Root].Lazytag) return;
        int Val=SegTree[Root].Lazytag;
        SegTree[Root].Lazytag=0;
        SegTree[Root<<1].Value+=Val;
        SegTree[Root<<1].Lazytag+=Val;
        SegTree[Root<<1|1].Value+=Val;
        SegTree[Root<<1|1].Lazytag+=Val;
        return;
    }
    
    void Update(int Root,int L,int R,int UL,int UR,int Add){
        if(R<UL || UR<L) return;
        if(UL<=L && R<=UR){
            SegTree[Root].Value+=Add;
            SegTree[Root].Lazytag+=Add;
            return;
        }
        Push_Down(Root);
        int mid=(L+R)>>1;
        Update(Root<<1,L,mid,UL,UR,Add);
        Update(Root<<1|1,mid+1,R,UL,UR,Add);
        SegTree[Root].Value=max(SegTree[Root<<1].Value,SegTree[Root<<1|1].Value);
        return;
    }
    
    int Query(int Root,int L,int R,int QL,int QR){
        if(R<QL || QR<L) return 0;
        if(QL<=L && R<=QR) return SegTree[Root].Value;
        Push_Down(Root);
        int mid=(L+R)>>1;
        return max(Query(Root<<1,L,mid,QL,QR),Query(Root<<1|1,mid+1,R,QL,QR));
    }
    
    inline int Calc(int x,int y){
        if(y!=K) Update(1,1,M,max(y-K,K),y-1,Matrix[x-1][y-K]);
        if(y!=K) Update(1,1,M,y,min(y+K-1,M),-Matrix[x-1][y]);
        return Query(1,1,M,y-K+1,min(y+K-1,M))+Sum[x][y]-Sum[x-2][y]-Sum[x][y-K]+Sum[x-2][y-K];
    }
    
    int main(){
        Read(N);Read(M);Read(K);
        for(RG i=1;i<=N;++i)
            for(RG j=1;j<=M;++j)
                Read(Matrix[N-i+2][j]);
        for(RG i=1;i<=N+1;++i)
            for(RG j=1;j<=M;++j)
                Sum[i][j]=Sum[i-1][j]+Sum[i][j-1]-Sum[i-1][j-1]+Matrix[i][j];
        for(RG i=K;i<=M;++i)
            dp[2][i]=Sum[2][i]-Sum[0][i]-Sum[2][i-K]+Sum[0][i-K];
        for(RG j=1;j<=M;++j){
            Pre[2][j]=max(Pre[2][j-1],dp[2][j]);
            Suf[2][M-j+1]=max(Suf[2][M-j+2],dp[2][M-j+1]);
            if(K<=j && j<=2*K) Data[j]=dp[2][j]-(Sum[2][K]-Sum[2][j-K]-Sum[1][K]+Sum[1][j-K]);
            else Data[j]=dp[2][j];
        }
        for(RG i=3;i<=N+1;++i){
            Build_SegTree(1,1,M);
            for(RG j=K;j<=M;++j){
                dp[i][j]=Pre[i-1][j-K]+Sum[i][j]-Sum[i-2][j]-Sum[i][j-K]+Sum[i-2][j-K];
                if(j+K<=M) dp[i][j]=max(dp[i][j],Suf[i-1][j+K]+Sum[i][j]-Sum[i-2][j]-Sum[i][j-K]+Sum[i-2][j-K]);
                dp[i][j]=max(dp[i][j],Calc(i,j));
            }
            for(RG j=1;j<=M;++j){
                Pre[i][j]=max(Pre[i][j-1],dp[i][j]);
                Suf[i][M-j+1]=max(Suf[i][M-j+2],dp[i][M-j+1]);
                if(K<=j && j<=2*K) Data[j]=dp[i][j]-(Sum[i][K]-Sum[i][j-K]-Sum[i-1][K]+Sum[i-1][j-K]);
                else Data[j]=dp[i][j];
            }
        }
        int Ans=0;
        for(RG i=K;i<=M;++i)
            Ans=max(Ans,dp[N+1][i]);
        printf("%d
    ",Ans);
        return 0;
    }
    
  • 相关阅读:
    openssl签署和自签署证书的多种实现方式
    OpenSSL主配置文件openssl.cnf
    openssl x509(签署和自签署)
    openssl rsautl和openssl pkeyutl(文件的非对称加密)
    openssl dgst(生成和验证数字签名)
    openssl passwd
    openssl speed和openssl rand
    openssl rsa/pkey
    openssl genrsa
    OpenSSL命令系列
  • 原文地址:https://www.cnblogs.com/AEMShana/p/12334724.html
Copyright © 2011-2022 走看看