题解: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\) 即可:

\[\begin{align*} g'_{i,x}&\gets \max(f_{i,x},g_{i,x})+1\\ g'_{i,x}&\gets g_{i-1,x\oplus 1}\\ g'_{i,x}&\gets f_{i-1,x\oplus 1}+1 \end{align*} \]

扫描完一整段后再用 \(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;
}
posted @ 2026-08-28 22:08  P2441M  阅读(11)  评论(0)    收藏  举报