1 条题解

  • 0
    @ 2026-4-20 20:04:48

    一、题意整理(关键约束)

    给定数组 aa,满足条件:

    max(ai)i=1nai\max(|a_i|) \le \sum_{i=1}^n a_i

    我们需要构造数组 bb,满足:

    1. aabb子序列
    2. a=b\sum a = \sum b(总和不变)。
    3. bb最大子段和(无聊值)尽可能小
    4. 在满足条件3的前提下,bb 的长度 mm 尽可能小

    求:最终数组 bb 的最小长度 mm


    二、核心结论(解题关键)

    这道题的本质是:用最少的插入次数,消除所有会导致最大子段和超标的区间

    1. 最大子段和下界

    题目给出的约束 max(ai)a\max(|a_i|) \le \sum a 保证了:

    • 数组总和 S=a>0S = \sum a > 0
    • 我们能构造出 bb,使其最大子段和严格等于 SS(这是理论最小值,无法更小)。

    2. 最小长度公式(标程核心)

    最终答案:

    m=n+v+extra\boldsymbol{m = n + v + extra}
    • nn:原数组长度
    • vv:标程中统计的需要插入分隔的段数
    • extraextra:最后一段是否需要补 0011 个分隔(r != p

    三、算法思路(标程逻辑)

    我们遍历数组,维护两个变量:

    • ll:当前区间可达的最小前缀和
    • rr:当前区间可达的最大前缀和
    • p=Sp = S:数组总和

    规则

    1. 遇到正数

      • 扩大区间:l+=ai, r=min(r+ai, p)l += a_i,\ r = \min(r + a_i,\ p)
      • l>rl > r,说明必须插入元素分隔v+=1v \boldsymbol{+}= 1
    2. 遇到负数

      • 缩小区间:r=ai, l=max(lai, 0)r -= |a_i|,\ l = \max(l - |a_i|,\ 0)
      • l>rl > r,说明必须插入元素分隔v+=1v \boldsymbol{+}= 1
    3. 最终答案

      m=n+v+(rp)m = n + v + (r \neq p)

    四、标程代码逐行详解

    #include <bits/stdc++.h>
     
    namespace FastIO {
    	// 快速读写模板(应对大数据输入)
    	template <typename T> inline T read() { ... }
    	template <typename T> inline void write(T x) { ... }
    }; using namespace FastIO;
     
    #define MAXN 3000001
    int a[MAXN]; // 数组开足 3e6 空间
    

    核心函数 solve()

    void solve() {
    	int N = read<int>();         // 读入数组长度 n
    	long long p = 0, l = 0, r = 0, v = 0;
    
    	// 第一步:计算数组总和 p = sum(a)
    	for (int i = 1; i <= N; ++i)
    		p += (a[i] = read<int>());
    
    	// 第二步:遍历数组,维护 [l, r] 区间
    	for (int i = 1; i <= N; ++i) {
    		if (a[i] >= 0) {
    			// 处理正数
    			l += a[i];
    			r = std::min(r + a[i], p);
    			
    			// 区间非法 → 必须插入分隔,v+1
    			if (l > r) {
    				++v;
    				l = a[i];
    				r = p;
    			}
    		} else {
    			// 处理负数,取绝对值
    			a[i] = -a[i];
    			r -= a[i];
    			l = std::max(l - a[i], 0ll);
    			
    			// 区间非法 → 必须插入分隔,v+1
    			if (l > r) {
    				++v;
    				l = 0;
    				r = p - a[i];
    			}
    		}
    	}
    
    	// 第三步:计算最终答案
    	print<int>(v + N + (int)(r != p), '\n');
    }
    

    五、公式解释(最关键)

    m=n+v+(rp)\boldsymbol{m = n + v + (r \neq p)}
    1. nn:原数组必须完整保留。
    2. vv:每触发一次区间非法,必须插入 1 个元素分隔。
    3. (rp)(r \neq p):最后一段如果没有闭合,额外补 1 个插入

    六、样例验证

    样例 2

    输入:

    4
    2 -3 2 2
    
    • n=4n=4
    • 遍历过程触发 v=2v=2
    • 最后 rpr \neq p,补 11
    • 答案:4+2+0=64 + 2 + 0 = \boldsymbol{6}(与样例完全一致)

    七、时间复杂度

    • 每组用例:O(n)O(n)
    • nn 之和 3×106\le 3\times10^6
    • 完美适配 3 秒时限

    八、总结(一句话记住)

    1. 目标:让最大子段和 =S= S(总和)。
    2. 方法:遍历维护前缀和区间 [l,r][l, r]
    3. 统计:区间非法时 v+1v+1(必须插入)。
    4. 答案:m=n+v+(rp)\boldsymbol{m = n + v + (r \neq p)}

    • 1

    信息

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