zoukankan      html  css  js  c++  java
  • BZOJ3675 & 洛谷3648 & UOJ104:[Apio2014]序列分割——题解

    https://www.lydsy.com/JudgeOnline/problem.php?id=3675

    https://www.luogu.org/problemnew/show/P3648

    http://uoj.ac/problem/104

    PS:题面与题解针对于洛谷与uoj版本,bzoj请自觉把“输出做法”删去。

    小H最近迷上了一个分隔序列的游戏。在这个游戏里,小H需要将一个长度为n的非负整数序列分割成k+1个非空的子序列。为了得到k+1个子序列,小H需要重复k次以下的步骤:
    1.小H首先选择一个长度超过1的序列(一开始小H只有一个长度为n的序列——也就是一开始得到的整个序列);
    2.选择一个位置,并通过这个位置将这个序列分割成连续的两个非空的新序列。
    每次进行上述步骤之后,小H将会得到一定的分数。这个分数为两个新序列中元素和的乘积。小H希望选择一种最佳的分割方式,使得k轮之后,小H的总得分最大。

    参考:洛谷题解(虽然不算参考emmm只是斜率优化写跪了来debug用的)。

    不难证出只要分割点固定那么答案固定,因此O(kn^2)不难想。

    令s[i]表示前i项前缀和,那么我们有:

    f[k][i]=max(f[k][i],f[k-1][j]+(s[i]-s[j])*s[j])

    这个式子显然可以斜率优化,维护一个单调不增序列,令k<j<i。

    忽略f的前一维,则当f[k]+(s[i]-s[k])*s[k]<=f[j]+(s[i]-s[j])*s[j],把k从队首弹出。

    所以g[k,j]=(f[k]-f[j]+sqr(s[j])-sqr(s[k]))/(s[j]-s[k])<=s[i]时弹出k即可。

    同时考虑g[k,j]>=g[j,i]时把j弹出,给出证明。

    显然当g[j,i]<=s[i]时j不优。

    当g[k,j]>=g[j,i]>s[i]时k比j优仍然把j弹出。

    //我还naive的想要维护单调不减序列结果各种奇葩错误emmm……

    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    using namespace std;
    typedef long long ll;
    typedef double dl;
    const int N=1e5+5;
    const int K=205;
    inline int read(){
        int 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();
        return w?-X:X;
    }
    ll f[2][N];
    int n,k,nxt[N][K],q[N],s[N],l,r,now=1,pre=0;
    inline ll sqr(ll k){return k*k;}
    inline dl suan(int j,int k){
        if(s[j]==s[k])return -1e18;
        int i=pre;
        return (f[i][k]-f[i][j]+sqr(s[j])-sqr(s[k]))/(dl)(s[j]-s[k]);
    }
    int main(){
        n=read(),k=read();
        for(int i=1;i<=n;i++)s[i]=s[i-1]+read();
        for(int j=1;j<=k;j++){
            now^=1,pre^=1;l=r=0;
            for(int i=1;i<=n;i++){
                while(l<r&&suan(q[l],q[l+1])<=(dl)s[i])l++;
                int t=q[l];
                f[now][i]=f[pre][t]+(ll)(s[i]-s[t])*s[t];nxt[i][j]=t;
                while(l<r&&suan(q[r-1],q[r])>=suan(q[r],i))r--;
                q[++r]=i;
            }
        }
        printf("%lld
    ",f[now][n]);
        int tmp=nxt[n][k];
        while(k){
            printf("%d ",tmp);
            tmp=nxt[tmp][--k];
        }
        return 0;
    }

    +++++++++++++++++++++++++++++++++++++++++++

    +本文作者:luyouqi233。               +

    +欢迎访问我的博客:http://www.cnblogs.com/luyouqi233/+

    +++++++++++++++++++++++++++++++++++++++++++

  • 相关阅读:
    Educational Codeforces Round 97 (Rated for Div. 2)
    2020 计蒜之道 线上决赛
    kuangbin 莫队专题
    Codeforces Round #677 (Div. 3)
    Codeforces Round #674 (Div. 3)
    Elasticsearch Alias:别名
    Elasticsearch 集群重要配置的修改
    Elasticsearch文档版本冲突原理与解决
    redis操作详情
    对密码必须包含字母,数字,特殊字符正则表达式理解
  • 原文地址:https://www.cnblogs.com/luyouqi233/p/8893754.html
Copyright © 2011-2022 走看看