• [SDOI2016]征途


    题目大意:
      给你一个长度为n的数列,让你分成m段,使得每一段数字和的方差最小。
      输出最小方差*m*m。

    思路:
      不难想到一个DP。
      设f[i][k]表示前i个数分成k段,则
      f[i][k]=std::min(f[i][k],m*sqr(sum[i])-2*sum[n]*sum[i]+f[j][k-1]+2*sum[n]*sum[j]-2*m*sum[i]*sum[j]+m*sqr(sum[j]));
      答案即为sqr(sum[n])+f[n][m]。
      复杂度O(n^3)。
      一开始把方程往奇怪的方向推,最后没弄出来斜率式。
      实际上如果我们用i和j表示可以被转移的两个状态,那么我们只需要维护一个关于斜率(f[i][!(k&1)]-f[j][!(k&1)]+sqr(sum[i])-sqr(sum[j]))/(sum[i]-sum[j])单调递增的队列。
      这样就可以优化到O(n^2)。

     1 #include<cstdio>
     2 #include<cctype>
     3 typedef long long int64;
     4 inline int getint() {
     5     register char ch;
     6     while(!isdigit(ch=getchar()));
     7     register int x=ch^'0';
     8     while(isdigit(ch=getchar())) x=(((x<<2)+x)<<1)+(ch^'0');
     9     return x;
    10 }
    11 const int64 inf=0x7ffffffffffffffll;
    12 const int N=3001;
    13 int q[N],h,t;
    14 int64 sum[N],f[N][2];
    15 inline int64 sqr(const int64 &x) {
    16     return x*x;
    17 }
    18 inline double calc(const int &i,const int &j,const int &k) {
    19     return double(f[i][!(k&1)]-f[j][!(k&1)]+sqr(sum[i])-sqr(sum[j]))/double(sum[i]-sum[j]);
    20 }
    21 int main() {
    22     const int n=getint(),m=getint();
    23     for(register int i=1;i<=n;i++) sum[i]=sum[i-1]+getint();
    24     for(register int i=1;i<=n;i++) f[i][0]=inf;
    25     for(register int k=1;k<=m;k++) {
    26         q[h=t=0]=k-1;
    27         for(register int i=k;i<=n;i++) {
    28             while(h<t&&calc(q[h+1],q[h],k)<=2*sum[i]) h++;
    29             f[i][k&1]=f[q[h]][!(k&1)]+sqr(sum[i]-sum[q[h]]);
    30             while(h<t&&calc(i,q[t],k)<=calc(q[t],q[t-1],k)) t--;
    31             q[++t]=i;
    32         }
    33     }
    34     printf("%lld
    ",f[n][m&1]*m-sqr(sum[n]));
    35     return 0;
    36 }
  • 相关阅读:
    VM439:1 https://unidemo.dcloud.net.cn 不在以下 request 合法域名列表中,请参考
    vue点击出现蒙版
    vue实现轮播图
    js函数调用的几种方法
    js中的object类型
    vue报错笔记
    vue中项目如何引入sass (vue-cli项目)
    vue编写轮播图组件
    VSCode配置 Debugger for Chrome插件
    就是一段程序,可以求出N个不等长列表中取N个元素形成的所有组合
  • 原文地址:https://www.cnblogs.com/skylee03/p/8084788.html
Copyright © 2020-2023  润新知