题解:AT_arc178_d [ARC178D] Delete Range Mex

简单题。

题意:给出一个长为 \(m\) 的已有序列 \(A\),问有多少个 \([0,n-1] 的\)排列 \(P\) 可以经过以下操作得到 \(A\)

  • 选取现在一个区间 \([l,r]\),假如序列中存在 \(\operatorname{mex}(P_l,P_{l+1},\cdots P_r)\),那么可以删掉这个数。

\(n\le 500\)

做法:先手玩序列,发现从大往小删一定是最优的,因为不会影响更小的是否可以删除。

考虑从小往大加,发现能加的要求是 \([0,x-1]\) 都在 \(x\) 的同一侧。

那么我们考虑记 \(dp_{i,l,r}\) 表示填完 \(i\) 后,目前已经占据了 \([l,r]\) 这些空,这里空指的是端点间的空段。因为我往空段里放数的方式是唯一的。

如果填一个已经有数的,那么 \(l \gets \min(l,pos_x),r\gets max(r, pos_x+1)\)

如果填一个没有的,那么 \(dp_{i,l,r}\to dp_{i,l,r,r+1\cdots m+1},dp_{i, 1,2,\cdots l, r}\)

直接转移即可。复杂度 \(O(n^3)\)。注意需要在开始特判 \(0\) 的放置。

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int maxn = 505, mod = 998244353;
int n, m, a[maxn], dp[maxn][maxn][maxn], use[maxn], pos[maxn];
signed main() {
	cin >> n >> m;
	for (int i = 1; i <= m; i++)
		cin >> a[i], pos[a[i]] = i, use[a[i]] = 1;
	if(!use[0]) {
		for (int i = 1; i <= m + 1; i++)
			dp[0][i][i] = 1;
	}
	else 
		dp[0][pos[0]][pos[0] + 1] = 1;
	for (int i = 1; i < n; i++) {
		if(!use[i]) {
			for (int r = 1; r <= m + 1; r++) {
				int s = 0;
				for (int l = r; l >= 1; l--)
					s = (s + dp[i - 1][l][r]) % mod, dp[i][l][r] = (s + dp[i][l][r]) % mod;
			}
			for (int l = m + 1; l >= 1; l--) {
				int s = 0;
				for (int r = l; r <= m + 1; r++)
					s = (s + dp[i - 1][l][r]) % mod, dp[i][l][r] = (s + dp[i][l][r]) % mod;
			}
		}
		else {
			for (int l = 1; l <= m + 1; l++)
				for (int r = l; r <= m + 1; r++)
					dp[i][min(l, pos[i])][max(r, pos[i] + 1)] = (dp[i][min(l, pos[i])][max(r, pos[i] + 1)] + dp[i - 1][l][r]) % mod;
		}
//		for (int l = 1; l <= m + 1; l++)
//				for (int r = l; r <= m + 1; r++)
//					cout << i << " " << l << " " << r << " " << dp[i][l][r] << endl;
	}
	cout << dp[n - 1][1][m + 1] << endl;
	return 0;
}
posted @ 2025-11-17 21:30  LUlululu1616  阅读(5)  评论(0)    收藏  举报