2 条题解

  • 0
    @ 2026-4-30 16:31:25

    考虑删砝码的过程,若无论删 [l,r][l,r] 哪边都不行,一定是 [l,r1][l,r-1][l+1,r][l+1,r] 重心不在区间上。

    故有解的充要条件为对于每一个 i[1,n]i\in [1,n],都存在一个长度为 ii 的区间,其重心在答案区间上。必要性是显然的,充分性的话如果 i,i1i,i-1 两个长度都有合法的区间,i1i-1 的区间中必有长度为 ii 的子区间,因为如果不是将其移动到 ii 子区间内必然更居中于答案区间。

    一种暴力的方法是将所有长度的区间抠出来,枚举重心最小值,记录重心最大值,然后就有 O(n2logn)O(n^2\log n) 的做法。

    [1,n][1,n] 区间的重心为 midmid,无论是猜测还是观察都能想到我们会选重心更接近 midmid 的区间。而这里的接近是 mid\le mid 中最大的以及 mid\ge mid 中最小的。

    证明大概是这样,如图红色为重心,假设我们向右选了第三条远离重心的我们如过回头向左,那么白白使右端点 max\max 增加,如果继续向右那就答案左端点不变,右端点还在增加。不如直接选第四条重心靠近 midmid 的区间。

    这样有用的区间被控在了 O(n)O(n) 范围内。复杂度 O(nlogn)O(n\log n)

    笔者实现较为简单粗暴,直接 nn 个队列和一个优先队列,实际会有较大常数。

    #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
      @ 2026-4-30 16:29:05

      首先有 mid=i=1nainmid=\dfrac{\sum_{i=1}^na_i}n 必定被包含。

      对于每个 ii,我们找到操作 ii 次后重心位置 mid\le mid 且最大的区间,设其重心为 LiL_i,以及重心位置 mid\ge mid 且最小的区间,设其重心为 RiR_i

      我们大胆猜测题目相当于对每个 ii 选择 ci=Lic_i=L_ici=Ric_i=R_i,然后最小化序列 cc 的极差,证明如下:

      假设 LiL_i 对应的区间为 [l,r1][l,r-1]RiR_i 对应的区间为 [l+1,r][l+1,r],那么容易发现 [l+1,r1][l+1,r-1] 必定是 Li+1L_{i+1}Ri+1R_{i+1},假设它是 Li+1L_{i+1}Ri+1R_{i+1} 的情况是同理的:

      • 假设我们选择了 ci=Lic_i=L_ici+1=Li+1c_{i+1}=L_{i+1},那么我们显然可以从 [l,r1][l,r-1] 走到 [l+1,r1][l+1,r-1]
      • 假设我们选择了 ci=Lic_i=L_ici+1=Ri+1c_{i+1}=R_{i+1},那么由于 Li<Li+1L_i<L_{i+1},故我们显然可以把 cic_i 调整成 Li+1L_{i+1} 而不会使答案更劣。
      • 假设我们选择了 ci=Ric_i=R_ici+1=Ri+1c_{i+1}=R_{i+1},则我们显然可以从 [l+1,r][l+1,r] 走到 [l+2,r][l+2,r]
      • 假设我们选择了 ci=Ric_i=R_ici+1=Li+1c_{i+1}=L_{i+1},则我们显然可以从 [l+1,r][l+1,r] 走到 [l+1,r1][l+1,r-1]

      因此我们只需将所有 [Li,Ri][L_i,R_i] 排序,然后从小到大枚举 cc 序列的最小值,维护 cc 序列的最大值即可,时间复杂度 O(nlogn)\mathcal O(n\log n)

      • 1

      信息

      ID
      7273
      时间
      2000ms
      内存
      1024MiB
      难度
      10
      标签
      递交数
      1
      已通过
      1
      上传者