zoukankan      html  css  js  c++  java
  • bzoj4518[Sdoi2016]征途

    bzoj4518[Sdoi2016]征途

    题意:

    n个数,分成m段使每段和的方差尽可能小。

    题解:

    朴素的dp方程:f[i,m]=f[j,m-1]+(sum[i]-sum[j])2,j∈[1,i-1](sum[i]-sum[j]不用减平均数的原因是最后可以化简成f[n,m]*m-sum[n])复杂度为O(n3)会T,因为有个平方,所以考虑斜率优化,化简得到f[j,m-1]比f[k,m-1]好当且仅当((f[j,m-1]+sum[j]2)-(f[k,m-1]+sum[k]2))<2*sum[i]*(sum[j]-sum[k]),因为递推时是从小到大的顺序,所以sum[j]-sum[k]<0,所以((f[j,m-1]+sum[j]2)-(f[k,m-1]+sum[k]2))/(sum[j]-sum[k])>2*sum[i],用单调队列即可。多的那一维可以用滚动数组省空间。

    代码:

     1 #include <cstdio>
     2 #include <cstring>
     3 #include <algorithm>
     4 #define maxn 5000
     5 #define inc(i,j,k) for(int i=j;i<=k;i++)
     6 using namespace std;
     7 
     8 long long x[maxn],y[maxn],sum[maxn],a[maxn];int q[maxn],n,m,l,r;
     9 inline long long sqr(long long x){return x*x;}
    10 inline double get(long long a1,long long a2){
    11     return (double)((x[a1]+sqr(sum[a1]))-(x[a2]+sqr(sum[a2])))/(double)(sum[a1]-sum[a2]);
    12 }
    13 int main(){
    14     scanf("%d%d",&n,&m); sum[0]=0; inc(i,1,n)scanf("%lld",&a[i]),sum[i]=sum[i-1]+a[i];
    15     inc(i,1,n)x[i]=sqr(sum[i]);
    16     inc(i,2,m){
    17         l=0,r=0; memset(y,0,sizeof(y)); x[1]=sqr(sum[1]); q[l]=1;
    18         inc(j,2,n){
    19             while(l<r&&get(q[l],q[l+1])<2*sum[j])l++;
    20             while(l<r&&get(q[r-1],q[r])>get(q[r],j))r--;
    21             q[++r]=j; y[j]=x[q[l]]+sqr(sum[j]-sum[q[l]]);
    22         } swap(x,y);
    23     }
    24     printf("%lld",(x[n])*(long long)m-sqr(sum[n]));
    25 }

    20160423

  • 相关阅读:
    linux awk命令详解
    世界上最差的系统就是linux,双击不能安装软件
    硬盘安装CentOS 6.0(超级详细图文教程)
    Vim+cscope+ctags+tags阅读源代码
    使用vim看代码:cscope
    解决面板里没有network manager图标的问题
    【b804】双栈排序
    【BZOJ 1002】[FJOI2007]轮状病毒
    【BZOJ 1004】 [HNOI2008]Cards
    【t018】派对
  • 原文地址:https://www.cnblogs.com/YuanZiming/p/5703328.html
Copyright © 2011-2022 走看看