题解:P8113 [Cnoi2021] 自我主义的平衡者

P8113 [Cnoi2021] 自我主义的平衡者 题解

谁家全排列写错了导致暴力分都没拿到啊!

通过数据范围倒推时间复杂度。

n105,一眼 O(nlogn)

再结合暴力打表,很容易发现规律:

a 从小到大排列时,取到最大值;当 a 从大到小排列时,取到最小值。

感性理解一下,以最小值为例。欲取到最小值,即要使得 b 中的 0 尽量多。若已知前 i 项的排列方式,必然选择之后的最大值,从而使得 ai+1>bi¯

接下来以最小值为例对结论进行证明:

记从大到小排序后新序列的第 i 项为 ai,前 i 项的平均值为 bi¯

先考虑两个排序后的相邻项 ai,ai+1,若交换这两项的顺序:

  1. bi=bi+1=0,交换不会产生任何影响。
  2. bi=0,bi+1=m,则有 bi1¯>aiai+1。交换后 bi=bi=0,则 bi¯=bi¯ai+1aibi+1=m,结果不变。
  3. bi=m,bi+1=0,则有 bi1¯ai,bi¯>ai+1。记交换后的序列变为 b
    1. ai+1<bi1¯bi=0,则 bi¯=bi1¯ai,则 bi1=m,对最终平均值无影响。
    2. ai+1bi1¯bi=m,则 bi¯=bi¯,不论 bi+10 还是 m,最终结果都不会变小。
  4. bi=bi+1=m,则有 aibi1¯,ai+1bi¯。由于 bi1m,所以 bi¯bi1¯,所以 ai+1bi1¯bi=mbi¯=bi¯aiai+1bi¯=bi¯,则 bi+1=m,最终结果不变。

因此对两个相邻项的交换不会使得答案变小。

而对两个非相邻项则可以通过多次相邻项的交换来达到(类似于冒泡排序)。

因此当 a 从大到小排列时,取到最小值。

最大值同理。

于是得到了我见过的最简单的蓝题代码(这题真的有蓝吗):

#include<bits/stdc++.h> #define MAXN 100010 using namespace std; int n, m; int a[MAXN]; double max_s, min_s; int main(){ scanf("%d%d",&n,&m); for(int i = 1; i <= n; i++) scanf("%d",&a[i]); sort(a + 1, a + n + 1); for(int i = 2; i <= n; i++){ double ave = max_s / (i - 1); if(ave > a[i]) max_s += 0; else max_s += m; } for(int i = n - 1; i >= 1; i--){ double ave = min_s / (n - i); if(ave > a[i]) min_s += 0; else min_s += m; } printf("%.2f %.2f\n",max_s / n, min_s / n); return 0; }

__EOF__

本文作者NightTide
本文链接https://www.cnblogs.com/NightTide/p/18434007.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   Night_Tide  阅读(5)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 25岁的心里话
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示