Liveddd's Blog

愛にできることはまだあるかい

P8424 [JOI Open 2022] 跷跷板(Seesaw)

很妙的贪心。

最开始的平均数是固定的,我们设为 $\overline a$。直接求极差很不方便,我们考虑求所有操作中使 $\overline a’$ 向两边分别偏移了多少。

我们考虑 $i$ 次操作之后,还剩下一个长度为 $n-i$ 的 $[l,r]$ 的序列。我们容易发现对于最优解的情况,去掉两边元素之后得到的区间 $[l_1,r_1],[l_2,r_2]$,分别满足为最大的 $\overline a_{l_1,r_1}\le \overline a$ 和最小的 $\overline a_{l_2,r_2}\ge \overline a$。 否则,将当前区间向左或向右平移,一定可以得到更优的答案。

直接处理出这样的 $[l_1,r_1],[l_2,r_2]$。根据上面的推论,我们每一步一定是取这两个区间之一。于是我们直接按照 $\overline a-\overline a_{l,r}$ 从大到小排序,然后枚举,直接钦定 $[l_1,r_1]$,对于 $\overline a_{l_1’,r_1’}\le \overline a_{l_1,r_1}$,我们选取所有 $[l_2’,r_2’]$ 即可。时间复杂度 $\mathcal O(n\log n)$。

Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#include<iostream>
#include<cstdio>
#include<algorithm>
#define ll long long
#define PDD pair<double,double>
using namespace std;
const int N=2e5+10;
const double INF=1e9,eps=1e-9;
template <class T>
inline void read(T &x)
{
x=0;bool f=0;
char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=1;ch=getchar();}
while(ch>='0'&&ch<='9')x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
if(f)x=~x+1;
}
template <class T,class ...T1>
inline void read(T &x,T1 &...x1)
{
read(x),read(x1...);
}
int n;
ll sum[N];
double ave;
PDD a[N];
inline double get(int l,int r){return 1.0*(sum[r]-sum[l-1])/(r-l+1);}
int main()
{
read(n);
for(int i=1,x;i<=n;i++)read(x),sum[i]=sum[i-1]+x;
ave=1.0*sum[n]/n;
int l=1,r=n;
for(int i=1;i<=n;i++)
{
l++;
if(get(l,r)>ave)l--,r--;
a[i]={ave-get(l,r),get(l+1,r+1)-ave};
}
sort(a+1,a+n);
double ans=INF,res=0;
for(int i=n-1;i;i--)ans=min(ans,a[i].first+res),res=max(res,a[i].second);
printf("%.10lf\n",ans);
return 0;
}