1 条题解

  • 0
    @ 2026-4-3 13:18:58

    C. 最长好数组 - 详细题解

    问题重述

    构造一个严格递增的数组 aa,满足相邻元素差严格递增,且所有元素在 [l,r][l, r] 范围内。求最大可能长度。


    贪心策略

    为了构造最长的好数组,我们应该让数组的增长尽可能,这样才能在有限区间 [l,r][l, r] 内放入更多元素。

    贪心构造方法

    • 第一个元素取最小值:a1=la_1 = l
    • 第二个元素取 a2=l+1a_2 = l + 1(最小可能的增长)
    • 第三个元素取 a3=l+1+2=l+3a_3 = l + 1 + 2 = l + 3(差值为 22
    • 第四个元素取 a4=l+3+3=l+6a_4 = l + 3 + 3 = l + 6(差值为 33
    • 以此类推...

    通项公式

    ii 个元素的值为:

    ai=l+i(i1)2a_i = l + \frac{i(i-1)}{2}

    推导过程

    dk=ak+1akd_k = a_{k+1} - a_k 为相邻差,则 d1=1,d2=2,d3=3,d_1 = 1, d_2 = 2, d_3 = 3, \dots

    $$\begin{aligned} a_i &= a_1 + d_1 + d_2 + \cdots + d_{i-1} \\ &= l + (1 + 2 + \cdots + (i-1)) \\ &= l + \frac{(i-1)i}{2} \end{aligned} $$

    正确性证明

    引理

    贪心构造的数组 aa 是最优的,即不存在比它更长的好数组。

    证明思路

    假设存在另一个好数组 bb,长度大于 aa

    由于 aa 是贪心构造的(每个位置都取最小可能值),必然存在某个位置 ii,使得:

    • 对于所有 j<ij < i,有 aj=bja_j = b_j
    • ai<bia_i < b_i(因为 aia_i 已是最小)

    n=len(a)+1n = \text{len}(a) + 1bb 的长度(假设只长 11),则:

    $$b_n - b_{n-1} > b_{n-1} - b_{n-2} \ge a_{n-1} - a_{n-2} $$

    这意味着我们可以将 bnb_n 附加到数组 aa 的末尾,构造出更长的数组,与 aa 的最大性矛盾。

    因此,贪心构造的数组 aa 就是最优解。


    问题转化

    由通项公式 an=l+n(n1)2a_n = l + \frac{n(n-1)}{2},我们需要找到最大的 nn 满足:

    l+n(n1)2rl + \frac{n(n-1)}{2} \le r

    b=rlb = r - l,则条件化为:

    n(n1)2b\frac{n(n-1)}{2} \le b

    即:

    n(n1)2bn(n-1) \le 2b

    求解方法

    方法一:解二次不等式

    n2n2b0n^2 - n - 2b \le 0

    解方程 n2n2b=0n^2 - n - 2b = 0

    n=1+1+8b2n = \frac{1 + \sqrt{1 + 8b}}{2}

    因此:

    $$n_{\max} = \left\lfloor \frac{1 + \sqrt{1 + 8b}}{2} \right\rfloor $$

    方法二:二分查找

    二分查找最大的 nn 满足 n(n1)2b\frac{n(n-1)}{2} \le b


    算法步骤

    1. 读入 llrr
    2. 计算 b=rlb = r - l
    3. 二分查找最大的 nn 使得 n(n1)2b\frac{n(n-1)}{2} \le b
    4. 输出 nn

    完整代码

    #include <bits/stdc++.h>
    using namespace std;
    
    int main() {
        int t;
        cin >> t;
        
        while (t--) {
            long long l, r;
            cin >> l >> r;
            
            long long b = r - l;  // 可用的增长空间
            
            // 二分查找最大的 n 满足 n*(n-1)/2 <= b
            long long left = 2, right = 1000000000;
            
            while (left < right) {
                long long mid = (left + right) / 2;
                if (mid * (mid - 1) / 2 <= b) {
                    left = mid + 1;
                } else {
                    right = mid;
                }
            }
            
            cout << left - 1 << endl;
        }
        
        return 0;
    }
    

    公式解法(更简洁)

    #include <bits/stdc++.h>
    using namespace std;
    
    int main() {
        int t;
        cin >> t;
        
        while (t--) {
            long long l, r;
            cin >> l >> r;
            
            long long b = r - l;
            long long n = (1 + sqrt(1 + 8 * b)) / 2;
            
            cout << n << endl;
        }
        
        return 0;
    }
    

    时间复杂度

    • 二分查找:O(logr)O(\log r) 每个测试用例
    • 公式解法:O(1)O(1) 每个测试用例
    • 总复杂度:O(tlogr)O(t \log r)O(t)O(t)

    示例验证

    ll rr b=rlb = r-l n(n1)/2bn(n-1)/2 \le b 的最大 nn 输出
    1 2 1 n=2n=2: 2×1/2=112\times1/2=1 \le 1 2
    5 4 n=3n=3: 3×2/2=343\times2/2=3 \le 4
    n=4n=4: 4×3/2=6>44\times3/2=6 > 4
    3
    2 0 n=1n=1: 1×0/2=001\times0/2=0 \le 0 1
    10 20 10 n=5n=5: 5×4/2=10105\times4/2=10 \le 10
    n=6n=6: 6×5/2=15>106\times5/2=15 > 10
    5
    1 10910^9 109110^9-1 解方程得 n44721n \approx 44721 44721

    与样例输出完全一致。

    • 1

    信息

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