题解:P17329 [ICPC 2018 Nanjing R] Lagrange the Chef
做了好久哦。
把 \(X\) 看作 \(0\),\(Y\) 看作 \(1\),其他数看作 \(2\)。
这个操作形式可以看作:保持一个长度为 \(L\) 的子序列相对顺序不变,其他位置任意排列,操作次数为 \(n-L\)。问题转化成保留尽可能长的子序列。
不妨考虑先固定一个长度为 \(k\)、只包含 \(0/1\) 的子序列 \(a_{p_1},\cdots,a_{p_k}\)。设 \(a_{p_i}\neq a_{p_{i+1}}\) 的 \(i\) 的个数为 \(c\),则一个显然的必要条件是 \(c\leq cnt_2\)。
对于一个满足 \(a_{p_i}\neq a_{p_{i+1}}\) 的 \(i\) 中,若 \(a_{p_i+1},\cdots,a_{p_{i+1}-1}\) 中不包含 \(2\),则必然要消耗一次操作把一个 \(2\) 移动到 \(p_i,p_{i+1}\) 之间。设有 \(d\) 个这样的 \(i\),则最小操作次数就是 \(cnt_0+cnt_1-k+d\)。
为了刻画两个元素之间是否有 \(2\),容易想到以 \(2\) 为分界线对原序列分段。
但是这里有一个问题:没有被选进子序列中的 \(0,1\) 一定有位置可放吗?考虑如果保证 \(c\geq 1\),也就是子序列中同时存在 \(0,1\),则移动了 \(d\) 个 \(2\) 之后,序列中一定存在 \(12\) 空隙和 \(02\) 空隙,我们把其他的数对应放进空隙里即可保证合法。
所以这里要特判 \(c=0\) 的情况,也就是子序列中只有一种数。以子序列中全是 \(0\) 为例,我们自然希望取 \(k=cnt_0\)。简单讨论可以发现:若原序列中存在不包含 \(0\) 的段,则所有 \(1\) 都可以塞进这个段里,最小操作次数为 \(cnt_1\);否则我们不得不移动一个 \(2\) 来隔开 \(0\) 和 \(1\),最小操作次数为 \(cnt_1+1\)。子序列中全是 \(1\) 同理。
接下来对于 \(1\leq c\leq cnt_2\),那么我们只需要最大化 \(k-d\) 的值。
假设当前的 \(0/1\) 子序列为 \(a_{p_1},\cdots,a_{p_k}\),我们向末尾添加 \(a_{p_{k+1}}\),分类讨论:
- \(a_{p_i}=a_{p_{i+1}}\):\(k-d\) 的值增加 \(1\)。
- \(a_{p_i}\neq a_{p_{i+1}}\),\(p_i\) 和 \(p_{i+1}\) 在同一个段内:\(k-d\) 的值不变。
- \(a_{p_i}\neq a_{p_{i+1}}\),\(p_i\) 和 \(p_{i+1}\) 在不同的段内:\(k-d\) 的值增加 \(1\)。
直接对着这个结构 DP。从左到右考虑每个段,令 \(f_{i,0/1}\) 表示子序列末尾元素位于已经考虑完的段中,\(c=i\),子序列末尾元素为 \(0/1\),此时 \(k-d\) 的最大值;\(g_{i,0/1}\) 则表示子序列末尾元素位于当前段内时 \(k-d\) 的最大值。转移时扫描段内元素,根据前面的分讨更新 \(g\) 即可:
扫描完一整段后再用 \(g\) 去更新 \(f\)。
时间复杂度为 \(\mathcal{O}(n^2)\)。常数很小。
主要代码
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using i128 = __int128;
using ui = unsigned int;
using ull = unsigned long long;
using u128 = unsigned __int128;
using ld = long double;
using pii = pair<int, int>;
const int MAXN = 5e3 + 5;
const int inf = 1e9;
template<typename T> T lowbit(T x) { return x & -x; }
template<typename T> void chkMin(T &x, T y) { x = y < x ? y : x; }
template<typename T> void chkMax(T &x, T y) { x = x < y ? y : x; }
constexpr int lg2(ll x) { return 63 ^ __builtin_clzll(x); }
constexpr ll bitCeil(ll x) { return x == 1 ? 1ll : 1ll << lg2(x - 1) + 1; }
int n, X, Y, a[MAXN], cnt[3];
vector<int> vec[MAXN];
int f[2][MAXN], g[2][MAXN];
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> n >> X >> Y;
for (int i = 1; i <= n; ++i) {
cin >> a[i];
if (a[i] == X) ++cnt[a[i] = 0];
else if (a[i] == Y) ++cnt[a[i] = 1];
else ++cnt[a[i] = 2];
}
if (!cnt[0] || !cnt[1]) {
cout << "0";
return 0;
} else if (!cnt[2]) {
cout << "-1";
return 0;
}
int p = 1;
for (int i = 1; i <= n; ++i) {
if (a[i] == 2) ++p;
else vec[p].emplace_back(a[i]);
}
for (int t : {0, 1}) fill(f[t], f[t] + cnt[2] + 1, -inf);
bool markX = false, markY = false;
for (int k = 1; k <= p; ++k) {
for (int t : {0, 1}) fill(g[t], g[t] + cnt[2] + 1, -inf);
bool visX = false, visY = false;
for (int x : vec[k]) {
if (!x) visX = true;
else visY = true;
for (int i = 0; i <= cnt[2]; ++i) {
int val = max({!i ? 1 : -inf, g[x][i] + 1, f[x][i] + 1});
if (i) chkMax(val, max(g[x ^ 1][i - 1], f[x ^ 1][i - 1] + 1));
chkMax(g[x][i], val);
}
}
for (int t : {0, 1}) {
for (int i = 0; i <= cnt[2]; ++i) chkMax(f[t][i], g[t][i]);
}
if (!visX) markX = true;
if (!visY) markY = true;
}
int ans = -inf;
for (int i = 1; i <= cnt[2]; ++i) chkMax(ans, max(f[0][i], f[1][i]));
cout << min({cnt[1] + !markX, cnt[0] + !markY, cnt[0] + cnt[1] - ans});
return 0;
}

浙公网安备 33010602011771号