>
TYOI N0597 - 完美记忆
2024-09-20 题解 约 1.2 千字

简要题意:给你一个由 B 和 W 构成字符串,每次操作可以删除一段由 xbxb 个 B 和 xwxw 个 W 构成的子串,求最多执行多少次操作。

结论:一段区间可以删空当且仅当存在一个非负整数 kk,使得区间内 B 的个数等于 k×xbk \times xb,W 的个数等于 k×xwk \times xw

len=xb+xwlen=xb+xwcnt0cnt_0 是前缀中 W 的个数,cnt1cnt_1 是前缀中 B 的个数。

容易设计 O(n2)O(n^2) 的 DP。

考虑优化,将位置按照 mod len\bmod \ len 分组,那么每次删的区间端点一定满足在同一组内,并且满足:

len×(cnt1,icnt1,j)(ij)×xb,j<ilen×(cnt0,icnt0,j)(ij)×xw,j<ilen \times (cnt_{1,i}-cnt_{1,j})\le(i-j)\times xb, j<i \\ len \times (cnt_{0,i}-cnt_{0,j})\le(i-j)\times xw, j<i

i,ji,j 拆开,设 a0,i=cnt0,iilen×xw,a1,i=cnt1,iilen×xba_{0,i}=cnt_{0,i}-\frac{i}{len}\times xw, a_{1,i}=cnt_{1,i}-\frac{i}{len}\times xb,限制条件变为 a0,ia0,j,a1,ia1,j,j<ia_{0,i}\le a_{0,j}, a_{1,i}\le a_{1,j}, j<i

显然这是一个三维偏序问题,我们使用 CDQ 分治,显然 j<ij<i 是不重要的,我们排序处理一维的状态,另一维状态通过数据结构维护,设当前处理的区间是 [l,r][l,r] 大致流程如下:

预先处理初始状态的贡献。
rl<lenr-l<len,说明该区间的所有 DP 值都是从前面继承来的。
递归求左区间的 DP 值,更新跨越中点的 DP 值,使用树状数组通过左区间的 DP 值更新右区间的 DP 值,然后继续递归求右区间的 DP 值。

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
typedef pair<int, int> PII;

#define fir first
#define sec second
#define ep emplace
#define eb emplace_back

#define lowbit(x) ((x) & (-(x)))

inline int read() {
	int x = 0, f = 1; char ch = getchar();
	for (; !isdigit(ch); ch = getchar()) if (ch == '-') f = -1;
	for (; isdigit(ch); ch = getchar()) x = (x << 1) + (x << 3) + (ch ^ 48);
	return x * f;
}

const int N = 500010, M = 1000000;

int n, xb, xw, len, cnt[N][2];
int f[N], tr[N << 1], stk[N];
char s[N];

struct Node {
	int b, w, id, bid;
	friend bool operator< (Node a, Node b) {
		if (a.bid != b.bid) return a.bid < b.bid;
		if (a.b != b.b) return a.b > b.b;
		if (a.w != b.w) return a.w > b.w;
		return a.id < b.id;
	}
} a[N], tmp[N];

bool rec(Node a, Node b) { return a.id < b.id; }
void modify(int u, int v) { for (; u; u -= lowbit(u)) tr[u] = max(tr[u], v); }

int query(int u) {
	int res = -0x3f3f3f3f;
	for (; u <= M; u += lowbit(u)) res = max(res, tr[u]);
	return res;
}

void clear(int u) { for (; u; u -= lowbit(u)) tr[u] = -0x3f3f3f3f; }

void cdq(int l, int r) {
	if (r - l < len) {
		for (int i = l + 1; i <= r; i ++ ) f[i] = max(f[i - 1], f[i]);
		return;
	}
	int mid = (l + r) >> 1;
	cdq(l, mid);
	for (int i = l; i <= r; i ++ ) tmp[i] = a[i];
	sort(a + l, a + r + 1);
	int p = l;
	for (int t = 0; t < len; t ++ ) {
		int top = 0;
		for (; p <= r && a[p].bid == t; p ++ ) {
			int i = a[p].id;
			if (i <= mid) modify(a[p].w, f[i] - i / len), stk[ ++ top] = p;
			else f[i] = max(f[i], query(a[p].w) + i / len);
		}
		while (top) clear(a[stk[top]].w), top -- ;
	}
	f[mid + 1] = max(f[mid + 1], f[mid]);
	for (int i = l; i <= r; i ++ ) a[i] = tmp[i];
	cdq(mid + 1, r);
}

int main() {
	n = read(), xb = read(), xw = read(), len = xb + xw;
	scanf("%s", s + 1);
	for (int i = 1; i <= n; i ++ ) {
		cnt[i][0] = cnt[i - 1][0];
		cnt[i][1] = cnt[i - 1][1];
		if (s[i] == 'W') cnt[i][0] ++ ;
		if (s[i] == 'B') cnt[i][1] ++ ;
	}
	for (int i = len; i <= n; i += len) {
		int k = i / len;
		int cw = cnt[i][0], cb = cnt[i][1];
		if (cw > xw * k || cb > xb * k) continue;
		f[i] = max(f[i], k);
	}
	for (int i = 1; i <= n; i ++ ) {
		a[i].b = cnt[i][1] - (i / len) * xb + n;
		a[i].w = cnt[i][0] - (i / len) * xw + n;
		a[i].id = i, a[i].bid = i % len;
	}
	for (int i = 1; i <= M; i ++ ) tr[i] = -0x3f3f3f3f;
	cdq(1, n);
	printf("%d\n", f[n]);
	return 0;
}

题目信息

难度 提高+/省选-
链接 N0597
来源 33OJ
TYOI N0597 - 完美记忆
本文作者
Sky390
发布于
2024-09-20
版权协议
转载或引用本文时请遵守许可协议,注明出处、不得用于商业用途!