http://acm.hdu.edu.cn/showproblem.php?pid=4923
给定一个序列a,元素由0,1组成,求一个序列b,元素在0~1之间,并且保证递增。输出最小的∑(ai−bi)2
对于每个由连续1开头,连续0结尾的段落有最优值x=a/a+b = sum/len (a为1的个数,b为0的个数),用栈维护各个段的x(d[i])值,如果当前x值小于前面一个段的x值,那么就要将两个段合并,相应调整sum和len.
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <string>
#include <queue>
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;
#define RD(x) scanf("%d",&x)
#define RD2(x,y) scanf("%d%d",&x,&y)
#define clr0(x) memset(x,0,sizeof(x))
typedef long long LL;
int a[100005],s[100005],l[100005],r[100005],top;
double d[100005];
int main(){
int n,_;
RD(_);
while(_--){
top = 0;s[0] = 0;
RD(n);
for(int i = 1;i <= n;++i){
RD(a[i]);s[i] = s[i-1]+a[i];
}
for(int i = 1;i <= n;){
int j = i;
d[++top] = a[i];
while (j < n && a[j] >= a[j+1])
d[top] += a[++j];
l[top] = i;
r[top] = j;
d[top] /= (j-i+1);
while (top > 1 && d[top] < d[top-1]){
r[top - 1] = r[top];
--top;
d[top] = 1.0*(s[r[top]] - s[l[top]-1]) / (r[top]-l[top]+1);
}
i = j+1;
}
double ans = 0;
for (int k=1;k<=top;++k)
for (int i=l[k];i<=r[k];++i) ans += (d[k]-a[i])*(d[k]-a[i]);
printf("%.6f\n",ans);
}
return 0;
}