1 条题解
-
0
CF1987F2 Interesting Problem (Hard Version) 题解
一、题意回顾
给定长度为 的数组 。每次操作:
- 选择一个下标 ,满足 且 ;
- 删除 和 ,并将剩余部分拼接。
求最多可以执行多少次操作。
,。
二、必要条件分析
考虑如果我们希望 最终能够被选中(即在其位置满足 并参与操作),那么它应当满足以下条件:
- :因为我们只能删除元素,若 不可能通过操作使 。
- 与 的奇偶性相同:每次操作只能删去相邻两个元素,下标的前后关系不变,因此操作不会改变下标的奇偶性。
- 需要在 这段前缀中恰好删去 个数,使得 移动到位置 并满足 。
三、区间 DP:
定义
设 表示要将区间 完全删空,最少需要在 这段前缀内操作多少次。
转移
首先需要判断 是否可以被选中(因为不能通过操作前面的数使得 被删去)。若奇偶性不同或 ,则该区间不可行,跳过。
设 ,即前缀需要删除的次数。
-
配对删除 与 :
当 ,或内部区间 需要的删除数满足条件:
此时可令 。
-
枚举断点 (,且 与 同奇偶步长,即只取奇数步):
$$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) $$其中 是因为左边区间被删掉后,右边区间所需的删除数会相应地减少。
初始化
所有 初始为 。只对长度为偶数的区间进行 DP。
四、答案 DP:
设 表示前缀 里最多能操作几次(即最大删除对数)。
转移:枚举前缀 的一段后缀 ( 与 的奇偶性保证区间长度为偶数)。若 ,则说明该区间可以被删空,有转移:
$$g_i = \max\!\Big(g_i,\; g_{j-1} + \frac{i - j + 1}{2}\Big) $$同时还要维护 。
最终答案即为 。
五、复杂度分析
区间 DP 的时间复杂度为 ,但实际常数很小,且仅对偶数长度区间进行 DP。对于 ,总计算量可控,可以通过。
答案 DP 为 。
总体复杂度 ,空间 。
六、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
- 上传者