1 条题解

  • 0
    @ 2026-10-31 17:58:11

    思路

    本题所有"奇数位/偶数位"都指原序列的绝对下标(不是子段内部的相对位置),这与样例和提示一致。

    为什么只减奇数位。 对任一子段,记 S=∑偶a−∑奇aS=\sum_{\text{偶}}a-\sum_{\text{奇}}a。减小偶数位只会让 SS 变小、约束更难满足;减小奇数位只会让 SS 变大。所以若存在某个最优解,把其中的偶数位操作换成对奇数位的操作不会让答案变差,因此总存在只减奇数位的最优解。

    局部约束就足够了。 对每个偶数下标 ii(2≤i≤n2\le i\le n,步长 22)考虑长度 33 的子段 [i−1,i,i+1][i-1,i,i+1](i=ni=n 时令哨兵 an+1=0a_{n+1}=0,等价于子段 [n−1,n][n-1,n]),它要求

    ai≥ai−1+ai+1.a_i\ge a_{i-1}+a_{i+1}.

    若所有这样的三元约束都成立,则任意长度 ≥2\ge2 的子段 [l,r][l,r] 都满足平衡条件:按 ll 的奇偶分类,把落在 [l,r][l,r] 内的每个偶数位的约束求和,内部奇数位会被相邻偶数位各覆盖一次、端点奇数位至少覆盖一次,子段外的项非负,于是偶数位之和 ≥\ge 奇数位之和。

    贪心。 从左到右处理每个偶数下标 ii:

    1. 令 odd=ai−1+ai+1odd=a_{i-1}+a_{i+1}、even=aieven=a_i。若 odd≤evenodd\le even,已满足;否则必须把相邻奇数位合计减少 d=odd−evend=odd-even。
    2. 只减奇数位,优先减右侧的 ai+1a_{i+1}(最多减到 00),不够再减左侧 ai−1a_{i-1},共操作 dd 次。

    右侧优先不劣于左侧优先:ai+1a_{i+1} 同时是下一个偶数位 i+2i+2 的左邻居,把它减得更小只会让后续约束更容易满足;两种做法对当前约束的减少量相同,所以优先减右侧是安全的。若改为从右往左遍历,按对称性优先减左侧即可。

    遍历完所有偶数位后所有局部三元约束成立,数组即为平衡序列;又由必要性,如此累计的操作次数就是最少的。

    每个测试用例 O(n)O(n),总复杂度 O(∑n)O(\sum n),空间复杂度 O(n)O(n)。

    代码

    #include<bits/stdc++.h>
    #define int long long
    using namespace std;
    
    void go(){
        int n;cin>>n;
        vector<int>a(n+2,0);        // 多开一位,a[n+1] 作为哨兵 0
        for(int i=1;i<=n;i++){
            cin>>a[i];
        }
    
        int sum = 0;                // 累计操作次数
        for(int i=2;i<=n;i+=2){     // 只检查偶数下标的局部约束
            int odd = a[i-1]+a[i+1];// 相邻两个奇数位之和
            int even = a[i];        // 当前偶数位
            if(odd>even){           // 不满足局部约束
                int d = odd-even;   // 需要减少的总量
                sum += d;           // 共需操作 d 次
                int t = min(a[i+1], d);// 优先从右侧奇数位减少
                a[i+1] -= t;
                d -= t;             // 更新还需减少的量
                t = min(a[i-1],d);  // 右侧不够再从左侧奇数位减少
                a[i-1] -= t;
    
            }
        }
        cout<<sum<<'\n';
        for(int i=1;i<=n;i++){
            cout<<a[i]<<' ';
        }
        cout<<'\n';
    }
    
    signed main() {
        ios::sync_with_stdio(false);
        cin.tie(nullptr);cout.tie(nullptr);
        int t=1;
        cin >>t;                    // 多组测试数据
        while(t--){
            go();
        }
        return 0;
    }
    
    • 1

    信息

    ID
    28
    时间
    1000ms
    内存
    256MiB
    难度
    3
    标签
    递交数
    1
    已通过
    1
    上传者