• [斜率优化dp] HDU 3507 Print Article


    题目大意

    给你一个序列 ({c_n}),以及一个正整数 (M),现在要将这个序列分割成连续的若干段,每一段的价值是 (left(sum_{i=1}^{k}c_i ight)^2+M),求最小价值。((nleq 500000,Mleq 1000))

    题解

    (Sum[n]=sum_{i=1}^{n}c_i)
    (dp[i]) 表示把前 (i) 个数分割成若干段的最小价值,那么有 (dp[i]=min{dp[j]+left(Sum[i]-Sum[j] ight)^2+M},j<i)
    整理后得 (dp[j]+Sum[j]^2=2Sum[i]Sum[j]+dp[i]-Sum[i]^2-M)
    发现 (Sum[i]) 单增,可以斜率优化。
    (y=dp[j]+Sum[j]^2,k=2Sum[i],x=Sum[j])
    原式转化成了 (y=kx+dp[i]-Sum[i]^2-M) ,因为要使dp值最小,所以相当于要使这条直线在 (y) 轴上的截距最小,使用单调队列维护一个下凸壳进行状态转移即可。时间复杂度 (O(n))

    Code

    #include <iostream>
    #include <algorithm>
    #include <cstring>
    #include <string>
    #include <cstdio>
    #include <vector>
    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);
    }
    
    LL dp[500005],Sum[500005];
    int Q[500005];
    int N,head,tail;LL M;
    
    inline LL x(int i){return Sum[i];}
    inline LL y(int i){return dp[i]+Sum[i]*Sum[i];}
    inline LL k(int i){return Sum[i]<<1;}
    
    inline void maintain(int i,int j){
        dp[i]=dp[j]+(Sum[i]-Sum[j])*(Sum[i]-Sum[j])+M;
    }
    
    LL Solve(){
        head=1;tail=0;
        Q[++tail]=0;
        for(RG i=1;i<=N;++i){
            while(tail-head+1>=2){
                int a=Q[head],b=Q[head+1];
                if(y(b)-y(a)<=k(i)*(x(b)-x(a))) ++head;
                else break;
            }
            maintain(i,Q[head]);
            while(tail-head+1>=2){
                int a=Q[tail-1],b=Q[tail];
                if((y(b)-y(a))*(x(i)-x(a))>=(y(i)-y(a))*(x(b)-x(a))) --tail;
                else break;
            }
            Q[++tail]=i;
        }
        return dp[N];
    }
    
    int main(){
        while(~scanf("%d%lld",&N,&M)){
            memset(dp,0x3f,sizeof(dp));
            dp[0]=0;
            for(RG i=1;i<=N;++i){
                Read(Sum[i]);
                Sum[i]+=Sum[i-1];
            }
            printf("%lld
    ",Solve());
        }
        return 0;
    }
    
  • 相关阅读:
    chkconfig命令
    PHP中的WebService
    MySQL 中联合查询效率分析
    javascript中json对象长度
    Replace Pioneer
    c++ 调用matlab程序
    ubuntu 安装 sublime
    一些地址收藏
    学习笔记草稿
    Redis Cluster 集群使用(3)
  • 原文地址:https://www.cnblogs.com/AEMShana/p/13389828.html
Copyright © 2020-2023  润新知