1 条题解

  • 0
    @ 2026-5-4 14:58:09

    CF1987F2 Interesting Problem (Hard Version) 题解


    一、题意回顾

    给定长度为 nn 的数组 aa。每次操作:

    1. 选择一个下标 ii,满足 1i<a1 \le i < |a|ai=ia_i = i
    2. 删除 aia_iai+1a_{i+1},并将剩余部分拼接。

    求最多可以执行多少次操作。

    n800n \le 800n800\sum n \le 800


    二、必要条件分析

    考虑如果我们希望 aia_i 最终能够被选中(即在其位置满足 ai=ia_i = i 并参与操作),那么它应当满足以下条件:

    1. aiia_i \le i:因为我们只能删除元素,若 ai>ia_i > i 不可能通过操作使 ai=ia_i = i
    2. aia_iii 的奇偶性相同:每次操作只能删去相邻两个元素,下标的前后关系不变,因此操作不会改变下标的奇偶性。
    3. 需要在 [1,i1][1, i-1] 这段前缀中恰好删去 iai2\dfrac{i - a_i}{2} 个数,使得 aia_i 移动到位置 ii 并满足 ai=ia_i = i

    三、区间 DP:fl,rf_{l, r}

    定义

    fl,rf_{l, r} 表示要将区间 [l,r][l, r] 完全删空,最少需要在 [1,l1][1, l-1] 这段前缀内操作多少次

    转移

    首先需要判断 ala_l 是否可以被选中(因为不能通过操作前面的数使得 ala_l 被删去)。若奇偶性不同或 l<all < a_l,则该区间不可行,跳过。

    v=lal2v = \dfrac{l - a_l}{2},即前缀需要删除的次数。

    1. 配对删除 ala_lara_r

      len=2len = 2,或内部区间 [l+1,r1][l+1, r-1] 需要的删除数满足条件:

      fl+1,r1vf_{l+1,\,r-1} \le v

      此时可令 fl,r=vf_{l, r} = v

    2. 枚举断点 kkl+1kr1l+1 \le k \le r-1,且 kkll 同奇偶步长,即只取奇数步):

      $$f_{l, r} = \min\!\Big(f_{l, r},\; \max\!\big(v,\ f_{l, k},\ f_{k+1, r} - \frac{k - l + 1}{2}\big)\Big) $$

      其中 fk+1,rkl+12f_{k+1, r} - \frac{k-l+1}{2} 是因为左边区间被删掉后,右边区间所需的删除数会相应地减少。

    初始化

    所有 fl,rf_{l, r} 初始为 ++\infty。只对长度为偶数的区间进行 DP。


    四、答案 DP:gig_i

    gig_i 表示前缀 [1,i][1, i]最多能操作几次(即最大删除对数)。

    转移:枚举前缀 [1,i][1, i] 的一段后缀 [j,i][j, i]jjii 的奇偶性保证区间长度为偶数)。若 fj,igj1f_{j, i} \le g_{j-1},则说明该区间可以被删空,有转移:

    $$g_i = \max\!\Big(g_i,\; g_{j-1} + \frac{i - j + 1}{2}\Big) $$

    同时还要维护 gi=max(gi,gi1)g_i = \max(g_i, g_{i-1})

    最终答案即为 gng_n


    五、复杂度分析

    区间 DP 的时间复杂度为 O(n3)O(n^3),但实际常数很小,且仅对偶数长度区间进行 DP。对于 n800n \le 800,总计算量可控,可以通过。

    答案 DP 为 O(n2)O(n^2)

    总体复杂度 O(n3)O(n^3),空间 O(n2)O(n^2)


    六、AC 代码

    #include<bits/stdc++.h>
    #define For(i, a, b) for(int i = (a); i <= (b); i++)
    #define Rof(i, a, b) for(int i = (a); i >= (b); i--)
    using namespace std;
    const int N = 805, INF = 1e9;
    int n, a[N], f[N][N], g[N];
    
    void Solve(){
        cin >> n;
        For(i, 1, n) cin >> a[i];
        For(l, 1, n) For(r, 1, n) f[l][r] = INF;
    
        // 区间 DP
        for(int len = 2; len <= n; len += 2)
            For(l, 1, n - len + 1){
                int r = l + len - 1, v = (l - a[l]) / 2;
                if((l % 2 != a[l] % 2) || l < a[l]) continue;
    
                // 配对删除 a_l 与 a_r
                if(len == 2 || f[l + 1][r - 1] * 2 <= (l - a[l]))
                    f[l][r] = v;
    
                // 枚举断点
                for(int k = l + 1; k <= r - 1; k += 2){
                    int nv = max({v, f[l][k], f[k + 1][r] - (k - l + 1) / 2});
                    f[l][r] = min(f[l][r], nv);
                }
            }
    
        // 答案 DP
        For(i, 1, n){
            g[i] = g[i - 1];
            for(int len = 1; 2 * len <= i; len++)
                if(f[i - 2 * len + 1][i] <= g[i - 2 * len])
                    g[i] = max(g[i], g[i - 2 * len] + len);
        }
    
        cout << g[n] << '\n';
    }
    
    signed main(){
        ios::sync_with_stdio(0);
        cin.tie(0); cout.tie(0);
        int T = 1; cin >> T;
        while(T--) Solve();
        return 0;
    }
    • 1

    信息

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