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; }