zoukankan      html  css  js  c++  java
  • 洛谷P3648 [APIO2014]序列分割(斜率优化)

    传送门

    没想到这种多个状态转移的还能用上斜率优化……学到了……

    首先我们可以发现,切的顺序对最终答案是没有影响的

    比方说有一个序列$abc$,每一个字母都代表几个数字,那么先切$ab$再切$bc$,得分是$ab+bc+ac$,而如果先切$bc$再切$ab$,得分也是$ab+bc+ac$,不难看出得分是一样的

    那么我们可以考虑一下转移方程$$dp[a][i]=max{dp[a-1][j]+sum[j]*(sum[i]-sum[j])}$$

    其中$a$表示切几刀,$sum$表示前缀和

    然后发现空间复杂度太大了,又发现每一刀的状态只与前一刀有关,那么可以用滚动数组优化

    然后上面的转移是$O(n^2k)$的,那么考虑用斜率优化优化到$O(nk)$(以下省略dp的第一维)

    我们假设$j>k$且$j$比$k$更优,则有$$dp[j]+sum[j]*(sum[i]-sum[j])>dp[k]+sum[k]*(sum[i]-sum[k])$$

    $$(dp[j]-sum[j]^2)-(dp[k]-sum[k]^2)>sum[i]*sum[k]-sum[i]*sum[j]$$

    因为$sum[k]-sum[j]$是负数,所以除的时候不等式要变号

    $$frac{(dp[j]-sum[j]^2)-(dp[k]-sum[k]^2)}{sum[k]-sum[j]}<sum[i]$$

    然后直接上斜率优化

    注意特判$sum[k]-sum[j]=0$,随便返回极大值或极小值

    顺便注意记录路径

     1 //minamoto
     2 #include<iostream>
     3 #include<cstdio>
     4 #define ll long long
     5 using namespace std;
     6 #define getc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
     7 char buf[1<<21],*p1=buf,*p2=buf;
     8 inline int read(){
     9     #define num ch-'0'
    10     char ch;bool flag=0;int res;
    11     while(!isdigit(ch=getc()))
    12     (ch=='-')&&(flag=true);
    13     for(res=num;isdigit(ch=getc());res=res*10+num);
    14     (flag)&&(res=-res);
    15     #undef num
    16     return res;
    17 }
    18 char sr[1<<21],z[20];int C=-1,Z;
    19 inline void Ot(){fwrite(sr,1,C+1,stdout),C=-1;}
    20 inline void print(int x){
    21     if(C>1<<20)Ot();if(x<0)sr[++C]=45,x=-x;
    22     while(z[++Z]=x%10+48,x/=10);
    23     while(sr[++C]=z[Z],--Z);sr[++C]=' ';
    24 }
    25 const int N=100005;
    26 int to[205][N],q[N],n,k,h,t,r;
    27 ll sum[N],dp[2][N];
    28 inline double slope(int j,int k){
    29     if(sum[j]==sum[k]) return 1e18;
    30     return ((dp[r^1][j]-sum[j]*sum[j])-(dp[r^1][k]-sum[k]*sum[k]))*1.0/(sum[k]-sum[j]);
    31 }
    32 int main(){
    33     //freopen("testdata.in","r",stdin);
    34     n=read(),k=read();
    35     for(int i=1;i<=n;++i) sum[i]=read()+sum[i-1];
    36     for(int a=1;a<=k;++a){
    37         r=a&1;
    38         h=t=0;
    39         for(int i=1;i<=n;++i){
    40             while(h<t&&slope(q[h],q[h+1])<sum[i]) ++h;
    41             to[a][i]=q[h];
    42             dp[r][i]=dp[r^1][q[h]]+sum[q[h]]*(sum[i]-sum[q[h]]);
    43             while(h<t&&slope(q[t],q[t-1])>slope(q[t-1],i)) --t;q[++t]=i;
    44         }
    45     }
    46     printf("%lld
    ",dp[k&1][n]);
    47     for(int i=k,u=n;i;--i){
    48         u=to[i][u];
    49         print(u);
    50     }
    51     Ot();
    52     return 0;
    53 }
  • 相关阅读:
    java 运算符的优先级比较
    Java String类和StringBuffer类的区别
    Java 并发编程
    java构造函数和初始化
    Java 动态绑定
    Java day3
    Java day2
    Java day1
    计算机系统原理之程序是怎么运行的 【转】
    MemberCached 学习上【转】
  • 原文地址:https://www.cnblogs.com/bztMinamoto/p/9546240.html
Copyright © 2011-2022 走看看