AtCoder Regular Contest 184 D Erase Balls 2D

转化计数对象。

直接数最终剩下的球的集合似乎并不好做。考虑数选择的球的集合(显然选择的顺序不重要,只有选择了哪些球重要)。

先把所有球按 \(x\) 坐标从小到大排序。设我们选择的球的下标为 \(i_1 < i_2 < \cdots < i_k\)。那么能选择这些球当且仅当 \(y_{i_1} > y_{i_2} > \cdots > y_{i_k}\)。因为若存在一对 \((p, q)\)\(p < q\))使得 \(y_{i_p} < y_{i_q}\),那么先选择任何一个都会导致另一个被删除。

但是很显然你直接这样数会算重,原因是一个最终剩下的球的集合可能对应多个选择的球的集合。考虑增加限制,数「选了集合外的任意一个球都会导致至少有一个球被删」的选择的球的集合。这样,选择的球的集合可以和最终剩下的球的集合一一映射。

判定一个选择的球的集合是否满足「选了集合外的任意一个球都会导致至少有一个球被删」,只需要判断对于所有相邻的球 \((i_p, i_{p + 1})\),把 \(i_p < j < i_{p + 1}\)\(y_{i_p} > y_j > y_{i_{p + 1}}\) 的球 \(j\) 拿出来,是否对于每个 \(j\) 都满足「在这些球中 \(j\) 前面有 \(y\) 比它小的球」或「在这些球中 \(j\) 后面有 \(y\) 比它大的球」。

考虑直接 DP(为了方便可以添加两个坐标分别为 \((0, n + 1), (n + 1, 0)\) 的球)。设 \(f_i\) 为考虑到前 \(i\) 个球且选了第 \(i\) 个球的方案数。转移枚举上一个在选择的球的集合内的球 \(j\)\(O(n)\) 判定 \(j\) 能否转移到 \(i\) 即可。

时间复杂度 \(O(n^3)\)

code
// Problem: D - Erase Balls 2D
// Contest: AtCoder - AtCoder Regular Contest 184
// URL: https://atcoder.jp/contests/arc184/tasks/arc184_d
// Memory Limit: 1024 MB
// Time Limit: 2000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include <bits/stdc++.h>
#define pb emplace_back
#define fst first
#define scd second
#define mkp make_pair
#define mems(a, x) memset((a), (x), sizeof(a))

using namespace std;
typedef long long ll;
typedef double db;
typedef unsigned long long ull;
typedef long double ldb;
typedef pair<ll, ll> pii;

const int maxn = 310;
const ll mod = 998244353;

ll n, a[maxn], f[maxn], b[maxn], pre[maxn], suf[maxn];

void solve() {
	scanf("%lld", &n);
	for (int _ = 0, x, y; _ < n; ++_) {
		scanf("%d%d", &x, &y);
		a[x] = y;
	}
	a[0] = n + 1;
	f[0] = 1;
	for (int i = 1; i <= n + 1; ++i) {
		for (int j = 0; j < i; ++j) {
			if (a[j] < a[i]) {
				continue;
			}
			int tot = 0;
			for (int k = j + 1; k < i; ++k) {
				if (a[j] > a[k] && a[k] > a[i]) {
					b[++tot] = a[k];
				}
			}
			pre[0] = 1e9;
			suf[tot + 1] = -1e9;
			for (int k = 1; k <= tot; ++k) {
				pre[k] = min(pre[k - 1], b[k]);
			}
			for (int k = tot; k; --k) {
				suf[k] = max(suf[k + 1], b[k]);
			}
			bool fl = 1;
			for (int k = 1; k <= tot && fl; ++k) {
				fl &= (pre[k - 1] < b[k] || b[k] < suf[k + 1]);
			}
			if (fl) {
				f[i] = (f[i] + f[j]) % mod;
			}
		}
	}
	printf("%lld\n", f[n + 1]);
}

int main() {
	int T = 1;
	// scanf("%d", &T);
	while (T--) {
		solve();
	}
	return 0;
}
posted @ 2024-09-23 16:55  zltzlt  阅读(25)  评论(0编辑  收藏  举报