P8445 射命丸文的取材之旅 题解
祝各位今年CSP-J/S RP++!
题意:给定序列 \(\{a_n\}\) 和 \(\{b_n\}\), 求一个序列\(\{c_n\}\) 满足 \(\forall i\) \(\in\) \([1,n]\), \(c_i\) \(\in\) \(\{a_i, b_i\}\), 并在序列 \(\{c_n\}\)中找到一段子区间\([l,r]\)使得其长度(\(l-r+1\))减去该区间的 \(mex\) 值最大. 输出这个最大值.
首先我们发现\(0 \le a_i,b_i \le n\), 所以我们可以考虑枚举 \(mex\{c_l,c_{l+1},...,c_{r-1},c_r\}\)的值, 然后就要解决如何找到一段最长的区间满足该区间的 \(mex\) 为我们所枚举到的\(mex\)值.
对此, 我们观察一下样例. 我们发现, 当 \(a_i=b_i\) 时, 我们的选择就固定了. 否则, 我们的选择完全由我们来决定. 换句话说, 真正确定该区间的 \(mex\) 值为多少的应为 \(a_i=b_i\) 时 \(a_i\) 的值.
但是, 如果枚举到一个 \(mex\) 都要去扫一遍的话, 铁定要超时, 所以, 我们可以做一个小小的预处理.
见下图:

我们发现, 假如我们枚举到的 \(mex\) 值是在 \(1,5,9\) 的位置被确定时, \(mex\) 值为我们所枚举到的值的区间只有两个—— \([2, 4]\) 和 \([6,8]\), 因此, 我们可以开 \(n\) 个 \(vector\) 数组, 来记录每一个被确定的数字分别在哪一个位置出现, 枚举到该值的时候循环一遍 \(vector\) 数组就好了.
for(int i = 1; i <= n; ++ i) {
if(a[i] == b[i]) {
V[a[i]].push_back(i);
}
}
查询的时候, 我们循环 \(vector\) 数组, 枚举每一个区间, 维护答案即可.
for(int i = 0; i <= n; ++i) {
int last = 0;
for(int j = 0; j < V[i].size(); ++ j) {
int now = V[i][j];
if(now - 1 >= last + 1) {
ans = max(ans, now - 1 - last - 1 + 1 - i);
}
last = now;
}
ans = max(ans, n - last - i);
}
外面的循环是 \(O(n)\) 的, 而内部的 \(vector\) 循环由于同一个下标不会被 \(push\) 两次, 所以内部总时间最多为 \(O(n)\).
\(ans\) 的初始值设为 \(0\) 即可. 因为无论如何答案都不可能为负数. 答案为 \(0\) 时也只有毒瘤情况, 如:
1
0
0

浙公网安备 33010602011771号