1 条题解

  • 0
    @ 2025-10-26 22:33:34

    题目理解

    我们有一个递增数列 a₁ ≤ a₂ ≤ ... ≤ aₙ。

    一次操作:选一个位置 i(不能是第一个或最后一个),把 aᵢ 变成 aᵢ₋₁ + aᵢ₊₁ - aᵢ。

    这个操作相当于交换相邻的差值:

    • 设 dᵢ = aᵢ₊₁ - aᵢ
    • 操作后,dᵢ₋₁ 和 dᵢ 交换位置

    所以我们可以任意重排 d₁, d₂, ..., dₙ₋₁ 的顺序。


    方差公式化简

    方差 D = (1/n) × Σ(aᵢ - 平均)²

    题目要求输出 n² × D 的最小值。

    经过数学推导,可以化简为: n² × D = n × Σaᵢ² - (Σaᵢ)²

    所以我们要最小化 n × Σaᵢ² - (Σaᵢ)²。


    关键发现

    经过推导发现,这个值只与差分的排列顺序有关,与 a₁ 无关。

    设 Sₖ = aₖ - a₁(即前 k-1 个差分的和),那么: 目标值 = n × ΣSₖ² - (ΣSₖ)²

    所以问题变成:如何排列差分,让这个值最小。


    最优排列方式

    通过分析,最优的排列是"单谷"形状:

    • 把差分排序后,先放一个小的,然后放一个大的,再放一个小的...
    • 或者反过来:先放大的,再放小的
    • 形成"小大小大..."或"大小大小..."的交替模式

    实际上,我们只需要尝试两种交替方式,取最小值即可。


    算法步骤

    1. 读入数列,计算差分 d[i] = a[i+1] - a[i]
    2. 把差分从小到大排序
    3. 用双端队列构造两种序列:
      • 方式1:奇数位置放左边,偶数位置放右边
      • 方式2:偶数位置放左边,奇数位置放右边
    4. 对每种方式计算目标值,取最小值

    代码实现

    #include <iostream>
    #include <algorithm>
    #include <deque>
    #include <vector>
    using namespace std;
    
    typedef long long ll;
    
    int main() {
        int n;
        cin >> n;
        
        vector<ll> a(n);
        for (int i = 0; i < n; i++) {
            cin >> a[i];
        }
        
        // 计算差分
        vector<ll> d(n-1);
        for (int i = 0; i < n-1; i++) {
            d[i] = a[i+1] - a[i];
        }
        
        // 排序差分
        sort(d.begin(), d.end());
        
        // 方法1:奇数下标放左边,偶数下标放右边
        deque<ll> q1;
        q1.push_back(0); // S1 = 0
        for (int i = 0; i < n-1; i++) {
            if (i % 2 == 0) {
                // 放右边
                q1.push_back(d[i] + q1.back());
            } else {
                // 放左边
                q1.push_front(d[i] + q1.front());
            }
        }
        
        // 计算目标值
        ll sumS = 0, sumS2 = 0;
        for (ll x : q1) {
            sumS += x;
            sumS2 += x * x;
        }
        ll ans = n * sumS2 - sumS * sumS;
        
        // 方法2:偶数下标放左边,奇数下标放右边
        deque<ll> q2;
        q2.push_back(0);
        for (int i = 0; i < n-1; i++) {
            if (i % 2 == 0) {
                // 放左边
                q2.push_front(d[i] + q2.front());
            } else {
                // 放右边
                q2.push_back(d[i] + q2.back());
            }
        }
        
        // 计算目标值
        sumS = 0, sumS2 = 0;
        for (ll x : q2) {
            sumS += x;
            sumS2 += x * x;
        }
        ans = min(ans, n * sumS2 - sumS * sumS);
        
        cout << ans << endl;
        
        return 0;
    }
    

    样例验证

    样例1:

    输入:
    4
    1 2 4 6
    输出:
    52
    

    与题目一致。


    复杂度分析

    • 排序差分:O(n log n)
    • 构造序列:O(n)
    • 计算答案:O(n)
    • 总体:O(n log n),对于 n ≤ 10000 完全可行

    这种方法利用了最优排列是单谷形状的性质,通过尝试两种交替方式找到最小方差。

    • 1

    信息

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