2 条题解
-
0
考虑删砝码的过程,若无论删 哪边都不行,一定是 和 重心不在区间上。
故有解的充要条件为对于每一个 ,都存在一个长度为 的区间,其重心在答案区间上。必要性是显然的,充分性的话如果 两个长度都有合法的区间, 的区间中必有长度为 的子区间,因为如果不是将其移动到 子区间内必然更居中于答案区间。
一种暴力的方法是将所有长度的区间抠出来,枚举重心最小值,记录重心最大值,然后就有 的做法。
设 区间的重心为 ,无论是猜测还是观察都能想到我们会选重心更接近 的区间。而这里的接近是 中最大的以及 中最小的。
证明大概是这样,如图红色为重心,假设我们向右选了第三条远离重心的我们如过回头向左,那么白白使右端点 增加,如果继续向右那就答案左端点不变,右端点还在增加。不如直接选第四条重心靠近 的区间。

这样有用的区间被控在了 范围内。复杂度 。
笔者实现较为简单粗暴,直接 个队列和一个优先队列,实际会有较大常数。
#include <bits/stdc++.h> #define dbz double #define pii pair<int, int> using namespace std; const int N = 2e5 + 3; int n; dbz a[N], s[N], mx, ans = 1e18; int L[N], R[N]; queue<dbz> q[N]; priority_queue<pair<dbz, int>> Q; signed main() { ios::sync_with_stdio(0); cin.tie(0), cout.tie(0); cin >> n; for (int i = 1; i <= n; ++i) cin >> a[i], s[i] = s[i - 1] + a[i]; dbz mid = 1.0 * s[n] / n; L[n] = 1, R[n] = n; q[n].push(mid); Q.push(make_pair(-mid, n)); mx = max(mx, mid); for (int i = n - 1; i >= 1; --i) { R[i] = n; for (int l = L[i + 1], r = l + i - 1; r <= R[i + 1]; ++l, ++r) { q[i].push(1.0 * (s[r] - s[l - 1]) / i); if (1.0 * (s[r] - s[l - 1]) / i <= mid) L[i] = max(L[i], l); else R[i] = min(R[i], r); } Q.push(make_pair(-q[i].front(), i)); mx = max(mx, q[i].front()); } while (1) { int u = Q.top().second; ans = min(ans, mx + Q.top().first); q[u].pop(), Q.pop(); if (q[u].empty()) break; mx = max(mx, q[u].front()); Q.push(make_pair(-q[u].front(), u)); } cout << fixed << setprecision(10) << ans << '\n'; return 0; } -
0
首先有 必定被包含。
对于每个 ,我们找到操作 次后重心位置 且最大的区间,设其重心为 ,以及重心位置 且最小的区间,设其重心为 。
我们大胆猜测题目相当于对每个 选择 或 ,然后最小化序列 的极差,证明如下:

假设 对应的区间为 , 对应的区间为 ,那么容易发现 必定是 或 ,假设它是 , 的情况是同理的:
- 假设我们选择了 且 ,那么我们显然可以从 走到 。
- 假设我们选择了 且 ,那么由于 ,故我们显然可以把 调整成 而不会使答案更劣。
- 假设我们选择了 且 ,则我们显然可以从 走到 。
- 假设我们选择了 且 ,则我们显然可以从 走到 。
因此我们只需将所有 排序,然后从小到大枚举 序列的最小值,维护 序列的最大值即可,时间复杂度 。
- 1
信息
- ID
- 7273
- 时间
- 2000ms
- 内存
- 1024MiB
- 难度
- 10
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者