P4072-[SDOI2016] 征途

P4072 [SDOI2016] 征途

Description

把一些数据分组,使得每组方差和最小。

Solution

设方差和为 Dx

m2×DX=mimxi2(imxi2),第二项没有影响,我们考虑对第一项求 min

fi 表示前 i 个数的平方和最小值。我们有 fi=min(fk+(sisk)2)。(ki

我们考虑斜率优化 dp

拆开式子有 fi=min(fk+si2+sk22sisk)si 为定值,暂不考虑。

则有 fi=fk+sk22sksi,我们考虑设 Akfk+sk2,设 Bk2sk,设 Cisi

由于 Bk,Ci 均单调,则我们考虑用双端队列维护答案和斜率。

我们考虑开一个双端队列,每次对于一个新的 i,先查询一个答案,对于当前状态,答案指针向前移动,找到一个当前 Ci 最先与凸包相切的点,而后我们考虑将这个答案插入凸包内,并持续判断队尾是否不优,如果不优,那么持续弹出,从而保证凸包内斜率的单调性。

由于每个点最多只能被作为答案或者删除一次,所以这个复杂度是 O(n)的。

复杂度 O(nm)

Code

#include<bits/stdc++.h>
using namespace std;

const int N=1e5+7;

int n,m;
int sum[N];
int qu[N];
int f[N],g[N];

double X(int i){
  return -2*sum[i];
}

double Y(int i){
  return g[i]+sum[i]*sum[i];
}

double check(int x,int y){
  return ((Y(x)-Y(y))/(X(x)-X(y)));
}

int main(){
  scanf("%d%d",&n,&m);
  for(int i=1;i<=n;i++){
    int temp;scanf("%d",&temp);sum[i]=sum[i-1]+temp;
    g[i]=sum[i]*sum[i];
  }
  for(int k=1;k<m;k++){
    int hd=1,tl=1;
    qu[1]=k;
    for(int i=k+1;i<=n;i++){
      while(hd<tl&&check(qu[hd],qu[hd+1])> -sum[i]) ++hd;
      int ans=qu[hd];
      f[i]=g[ans]+(sum[i]-sum[ans])*(sum[i]-sum[ans]);
      while(hd<tl&&check(qu[tl],qu[tl-1])<check(qu[tl],i)) --tl;
      qu[++tl]=i;
      // printf("%d %d %d %lf\n",ans,i,f[i],check(2,3));
    }
    for(int i=1;i<=n;i++) g[i]=f[i]; 
  }
  printf("%d",-sum[n]*sum[n]+m*f[n]);
  return 0;
}
posted @   Zimo_666  阅读(13)  评论(1编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示