P2115 [USACO14MAR] Sabotage G(二分/思维)

发布时间 2023-10-23 22:02:26作者: Zhang_Wenjie

题目 link

要求得到平均产奶量的最小值,想到二分答案。

emm...但是我在如何判断当前 \(mid\) 是否能成立上卡了好久,这里就需要思维了。

还是要想到本质上,可以试着用数学式子表达当前 \(mid\) 的合法条件,

若当前要删除 \([l,r]\) 的数,则有:(这里又可以想到用 前缀和 预处理)

\[\begin{aligned} \frac{S_n - Sr + S_{l-1}}{n-r+(l-1)}&\leqslant mid \\ S_n - Sr + S_{l-1}&\leqslant mid(n-r+l-1) \\ S_n - Sr + S_{l-1} -mid(n-r+l-1)&\leqslant 0 \\ \sum_{i=1}^{m}(a_i - mid)&\leqslant0~~~~~(m\in[1,l-1]\cup[r,n]) \end{aligned}\]

到这里应该是最简式子了。

而知道 \(l,r\) 是不定的,应该怎么取?如果这个式子成立,直观上肯定知道不可能所有取值都能满足,

也就是说,$$\exists~l,r\in[2,n-1],l\leqslant r,~~ \sum_{i=1}^{m}(a_i - mid)\leqslant0$$

那不就 一定有 :

\[\Bigg [\sum_{i=1}^{m}(a_i - mid)\Bigg ]_{min}\leqslant0 \]

所以 \(l,r\) 只要每次取到最小值,判断所有最小值中只要有一个成立就可取 \(mid\)

因此,思路也就来了,构建新的数组表达 \(a_i - mid\)

在此上,由于从中间删除,则需要求一个前缀和一个后缀和,同时分别维护最小值,

最后注意,头和尾不能删,也不能一个都不删。

#include <bits/stdc++.h>
#define re register int
#define min(x, y) (x < y ? x : y)
using namespace std;
typedef double db;
const int N = 1e5 + 10;
const db eps = 1e-5, inf = 0x3f3f3f3f;
int n;
db a[N], l, r, b[N], s1[N], s2[N], s1min[N], s2min[N];

bool check(db mid)
{
	for (re i = 0; i <= n + 1; i ++)
		s1min[i] = inf, s2min[i] = inf;
	for (re i = 1; i <= n; i ++) b[i] = a[i] - mid;
	for (re i = 1; i <= n; i ++)
	{
		s1[i] = s1[i - 1] + b[i];
		s1min[i] = min(s1min[i - 1], s1[i]);
	}	
	for (re i = n; i ; i --)
	{
		s2[i] = s2[i + 1] + b[i];
		s2min[i] = min(s2min[i + 1], s2[i]);
	}
	for (re i = 1; i <= n - 2; i ++)
	{
		if (s1min[i] + s2min[i + 2] <= 0) 
			return true;
	}
	return false;
}

void search()
{
	l = 1, r = N;
	while (l + eps < r)
	{
		db mid = (l + r) / 2;
		if (check(mid)) r = mid;
		else l = mid;
	}
}

int main()
{
	scanf("%d", & n);
	for (re i = 1; i <= n; i ++) cin >> a[i];
	search();
	printf("%.3lf\n", l);
	
	return 0;
}