Room and Moor
Time Limit: 12000/6000 MS (Java/Others) Memory Limit:
262144/262144 K (Java/Others)
Problem Description
PM Room defines a sequence A = {A1, A2,..., AN}, each of which is either 0 or 1. In order to beat him, programmer Moor has to construct another sequence B = {B1, B2,... , BN} of the same length,
which satisfies that:
Input
The input consists of multiple test cases. The number of test cases T(T<=100) occurs in the first line of input.
For each test case:
The first line contains a single integer N (1<=N<=100000), which denotes the length of A and B.
The second line consists of N integers, where the ith denotes Ai.
Output
Output the minimal f (A, B) when B is optimal and round it to 6 decimals.
Sample Input
4 9 1 1 1 1 1 0 0 1 1 9 1 1 0 0 1 1 1 1 1 4 0 0 1 1 4 0 1 1 1
Sample Output
1.428571 1.000000 0.000000 0.000000
题意:给出一个只包含0和1的A序列,找出一个B序列,使得f(A,B)最小,且Bi <= B(i+1), 0<= Bi <= 1。f(A,B)的计算方法如上图。
分析:分析可得,当Ai和Bi越接近时,f(A,B)越小。因为Bi<=B(i+1),所以我们可以分段来求。每一段中的Bi尽量取这一段Ai的平均值。但是有可能会出现前面的值大于后面的值的情况,这时,我们只需要把这两段合并为一段,每个Bi取这段Ai的平均值。
#include<cstdio> const int N = 100010; int sum[N], seg[N]; typedef __int64 LL; bool Check_Biger(int a1, int b1, int a2, int b2) { return (LL)(sum[b1] - sum[a1 - 1]) * (b2 - a2 + 1) >= (LL)(sum[b2] - sum[a2 - 1]) * (b1 - a1 + 1); } int main() { int T, n, i, a; scanf("%d",&T); while(T--) { scanf("%d",&n); sum[0] = 0; //sum[i]表示从A1到Ai一共有多少个1 for(i = 1; i <= n; i++) { scanf("%d",&a); sum[i] = sum[i-1] + a; } int length = 0, id; for(i = 1; i <= n; i++) { id = i; while(length > 0 && Check_Biger(seg[length], id-1, id, i)) { //如果前面一段的平均值不小于后面的平均值,则合并这两段 id = seg[length--]; } seg[++length] = id; } seg[++length] = n + 1; double ans = 0; for(i = 1; i < length; i++) { int l = seg[i]; //当前段的左端点 int r = seg[i+1] - 1; //当前段的右端点 int len = r - l + 1; //当前段的长度 double x = (double(sum[r]) - sum[l-1]) / len; //当前段每一个元素都取这一段的平均值 ans += sum[r] - sum[l-1] + len * x * x - 2 * (sum[r] - sum[l-1]) * x; //(Ai - x)^2 } printf("%lf\n", ans); } return 0; }
hdu 4923 Room and Moor(数学题)2014多校训练第6场