AT_arc184_d [ARC184D] Erase Balls 2D

AT_arc184_d [ARC184D] Erase Balls 2D

首先,假定我们选定的行为 \(i_1,i_2,\cdots,i_k\),并有 \(i_1<i_2<\cdots<i_k\),则 \(p_{i_1}>p_{i_2}>\cdots>p_{i_k}\)。其中 \(p_x\) 表示 \((x,p_x)\) 的位置上有点。

最终状态形如下图。显然,这种状态与操作顺序无关。

为了方便统计,加入 \((0,n+1)\)\((n+1,0)\),并钦定这两个点必须选上。

加入枚举选的点集 \(S=\{(i_1,p_{i_1}),(i_2,p_{i_2})\cdots,(i_k,p_{i_k})\}\),令最终保留的点集为 \(T\)。直接统计 \(S\) 的方案会算重,考虑对于每个点集 \(T\) 设定一个唯一的点集 \(S\) 与之对应。

对于每个 \(T\),一般的想法是选择 \(|S|\) 极值下的情况,考虑这两种方法是否成功:

  1. 选取 \(|S|\) 最少,考虑一例子:\((1,2),(2,1),(3,3)\),选取 \(S=\{(1,2)\}\) 或是 \(\{(2,1)\}\) 所对应的保留的点集 \(T\) 是相同,所以统计起来还需要额外增加条件。

  2. 选取 \(|S|\) 最多,这个 \(S\) 显然是唯一的。具体的,可以考虑一个结束状态 \(T\),通过不断将 \((x,p_x)\) 加入 \(S\) 中的方法,得到唯一性。

这样的话就比较容易了,考虑令 \(f_{i}\) 表示考虑了前 \(i\) 个,\((i,p_i) \in S\) 的总方案数。每次枚举上一个点 \(j\),判断 \((j,p_j)\)\((i,p_i)\) 所得到的矩形内的点是否满足:任意选择一个点,都会使得有一个矩形内的点被删除。

这个判断容易的,用前缀最小值与后缀最大值 \(O(n)\) 判断即可。

复杂度 \(O(n^3)\)。感觉 2804 的评分有点虚高……

#include <bits/stdc++.h>
using namespace std;

#define vi vector<int>
#define pb push_back
#define pii pair<int,int>
#define mkp make_pair

int rd() {
	int x = 0, f = 1;
	char ch = getchar();
	while (!('0' <= ch && ch <= '9')) {
		if (ch == '-') f = -1; ch = getchar();
	}
	while ('0' <= ch && ch <= '9') {
		x = (x << 1) + (x << 3) + (ch ^ 48); ch = getchar();
	}
	return x * f;
}

void wr(int x) {
	if (x < 0) putchar('-'), x = -x;
	if (x >= 10) wr(x / 10); putchar(x % 10 + '0');
}

const int N = 310;
const int mod = 998244353;

int f[N];
int n, x[N], y[N], pos[N];
int cnt, b[N], preb[N], sufb[N];

bool check() {
	for (int i = 1; i <= cnt; ++i)
		preb[i] = sufb[i] = b[i];
	for (int i = 2; i <= cnt; ++i)
		preb[i] = min(preb[i - 1], preb[i]);
	for (int i = cnt - 1; i >= 1; --i)
		sufb[i] = max(sufb[i + 1], sufb[i]);
	for (int i = 2; i <= cnt - 1; ++i) {
		if (preb[i] >= b[i] && b[i] >= sufb[i]) return false; 
	}
	return true;
}

int main() {
	ios::sync_with_stdio(false); cin.tie(0);
	cin >> n;
	for (int i = 1; i <= n; ++i) {
		int x, y; cin >> x >> y; pos[x] = y; 
	}
	f[0] = 1; pos[0] = n + 1;
	for (int i = 1; i <= n + 1; ++i) {
		for (int j = 0; j < i; ++j) {
			if (pos[i] > pos[j]) continue; 
			cnt = 0; 
			for (int k = j; k <= i; ++k) {
				if (pos[i] <= pos[k] && pos[k] <= pos[j])
					b[++cnt] = pos[k]; 
			}
			if (check()) f[i] = (f[i] + f[j]) % mod; 
		}
	}
	cout << f[n + 1] << endl; 
	return 0; 
}
posted @ 2024-09-29 20:51  wangzhongyuan  阅读(21)  评论(0)    收藏  举报