• bzoj 3675 [Apio2014]序列分割


    假设序列总和为tot, 每一段序列的和为 sum[i]

    那么每一段序列,都被乘了(tot-sum[i]),将所有序列的贡献累加起来再除以2就是答案

    考虑f[i][j]为前i个数,分了j块的最优值

    那么 f[i][j] = max(f[k][j-1]+(sum[i]-sum[k])*(tot-(sum[i]-sum[k]))) k ∈ [j-1, i-1];

    现在我们要优化这个转移 考虑如果从 k 转移比从 l 转移更优,那么可以化简式子 (f[k][j-1]-f[l][j-1])/(sum[k]-sum[l])-sum[k]-sum[l]>tot-2*sum[i]

    然后就可以斜率优化了,时间复杂度 O(Kn)

     1 #define MAXN 100010UL
     2 #include <cstdio>
     3 #define eps 1e-7
     4 
     5 using namespace std;
     6 
     7 typedef long long ll;
     8 
     9 int n, hd, tl, nw, lt = 1, K, q[MAXN];
    10 ll sum[MAXN], f[MAXN][2];
    11 
    12 ll Get_(ll x) {
    13     return x*(sum[n]-x);
    14 }
    15 
    16 double Get_k(int i, int j) {
    17     if(sum[i]==sum[j]) return 0;
    18     return (double)(f[i][lt]-f[j][lt])/(double)(sum[i]-sum[j])-sum[i]-sum[j];
    19 }
    20 
    21 bool Check(int i, int j, int k) {
    22     return Get_k(i, j)-Get_k(j, k)>-eps;
    23 }
    24 
    25 int main() {
    26     scanf("%d%d", &n, &K);
    27     ++ K;
    28     for(int i = 1 ; i <= n ; ++ i) scanf("%lld", &sum[i]), sum[i] += sum[i-1];
    29     for(int i = 1 ; i <= n ; ++ i) f[i][0] = sum[i]*(sum[n]-sum[i]);
    30     for(int k = 2 ; k <= K ; ++ k) {
    31         nw ^= 1, lt ^= 1;
    32         hd = tl = 0;
    33         q[tl ++] = k-1;
    34         for(int i = k ; i <= n ; ++ i) {
    35             while(hd+1<tl&&f[q[hd+1]][lt]+Get_(sum[i]-sum[q[hd+1]])>=f[q[hd]][lt]+Get_(sum[i]-sum[q[hd]])) ++ hd;
    36             f[i][nw] = f[q[hd]][lt]+Get_(sum[i]-sum[q[hd]]);
    37             while(hd+1<tl&&Check(i, q[tl-1], q[tl-2])) -- tl;
    38             q[tl ++] = i;
    39         }
    40     }
    41     printf("%lld", f[n][nw]/2);
    42     return 0;
    43 }
    View Code
  • 相关阅读:
    软件开发过程须贯彻评估和测试
    【灌水】多维成功论
    改进c系列(目录)
    网站管理艺术
    .net 跨平台也是一句谎言
    用户界面和逻辑应该分离
    设计模式
    程序员找不到工作是因为管理差
    编码阶段
    保证软件开发质量的一种管理学
  • 原文地址:https://www.cnblogs.com/assassain/p/5447362.html
Copyright © 2020-2023  润新知