专栏文章

题解:P4143 采集矿石

P4143题解参与者 1已保存评论 0

文章操作

快速查看文章及其快照的属性,并进行相关操作。

当前评论
0 条
当前快照
1 份
快照标识符
@mink13l9
此快照首次捕获于
2025/12/02 03:40
3 个月前
此快照最后确认于
2025/12/02 03:40
3 个月前
查看原文
我们考察钦定一个左端点,右端点越往右,排名单调减少,a\sum a 单调上升。所以 rka\operatorname{rk}-\sum a 是具有可二分性的,同时每个左端点最多对应一个右端点。
考虑枚举左端点二分出结果,此时只需要维护每个右端点的 rk \operatorname{rk}。以后缀排序后的顺序扫,则每次前 heighti\operatorname{height}_i 个的排名不变。因为后缀被排序了,第 heighti+1\operatorname{height}_i+1 个就是上一行的最后一个之后的排名,后面的排名是连续的。
可以做到 1log1\log2log2\log 能过。
CPP
#include <bits/stdc++.h>
using namespace std;
#define N 200005
#define int long long
int L[N << 2], R[N << 2], tag[N << 2], val[N << 2];

inline void cov(int u, int x) {
	if (~x)
		val[u] = (R[u] - L[u] + 1) * x, tag[u] = x;
}

inline void down(int u) {
	if (~tag[u])
		cov(u * 2, tag[u]), cov(u * 2 + 1, tag[u]), tag[u] = -1;
}

inline void up(int u) {
	val[u] = val[u * 2] + val[u * 2 + 1];
}

void build(int l, int r, int p) {
	L[p] = l, R[p] = r, tag[p] = -1;
	if (l == r)
		return;
	int m = (l + r) / 2;
	build(l, m, p * 2), build(m + 1, r, p * 2 + 1), up(p);
}

int qus(int l, int r, int p) {
	if (l <= L[p] && r >= R[p])
		return val[p];
	if (l > R[p] || r < L[p])
		return 0;
	down(p);
	return qus(l, r, p * 2) + qus(l, r, p * 2 + 1);
}

void add(int l, int r, int p, int v) {
	if (l <= L[p] && r >= R[p])
		return cov(p, v);
	if (l > R[p] || r < L[p])
		return;
	down(p), add(l, r, p * 2, v), add(l, r, p * 2 + 1, v), up(p);
}

inline int que(int r) {
	return qus(1, r, 1);
}
int ans[N], sa[N], rk[N << 1], hei[N], cnt[N], oldsa[N << 1], oldrk[N << 1], a[N], n, tot, res;
string s;

void SA() {
	for (int i = 1; i <= n; i++)
		cnt[rk[i] = s[i] - 'a' + 1]++;
	for (int i = 1; i <= 26; i++)
		cnt[i] += cnt[i - 1];
	for (int i = n; i; i--)
		sa[cnt[rk[i]]--] = i;
	for (int i = 1; i <= n; i++)
		oldrk[i] = rk[i];
	for (int i = 1, p = 0; i <= n; i++)
		rk[sa[i]] = (p += (oldrk[sa[i - 1]] != oldrk[sa[i]]));
	for (int d = 1; d < n; d <<= 1) {
		memset(cnt, 0, sizeof(cnt));
		for (int i = 1; i <= n; i++)
			cnt[rk[sa[i] + d]]++;
		for (int i = 1; i <= n; i++)
			cnt[i] += cnt[i - 1];
		for (int i = 1; i <= n; i++)
			oldsa[i] = sa[i];
		for (int i = n; i; i--)
			sa[cnt[rk[oldsa[i] + d]]--] = oldsa[i];
		memset(cnt, 0, sizeof(cnt));
		for (int i = 1; i <= n; i++)
			cnt[rk[sa[i]]]++;
		for (int i = 1; i <= n; i++)
			cnt[i] += cnt[i - 1];
		for (int i = 1; i <= n; i++)
			oldsa[i] = sa[i];
		for (int i = n; i; i--)
			sa[cnt[rk[oldsa[i]]]--] = oldsa[i];
		for (int i = 1; i <= n; i++)
			oldrk[i] = rk[i];
		for (int i = 1, p = 0; i <= n; i++)
			rk[sa[i]] = (p += (oldrk[sa[i - 1]] != oldrk[sa[i]] || oldrk[sa[i - 1] + d] != oldrk[sa[i] + d]));
	}
	for (int i = 1, k = 0; i <= n; i++) {
		if (rk[i] == 0)
			continue;
		if (k)
			k--;
		while (s[i + k] == s[sa[rk[i] - 1] + k])
			k++;
		hei[rk[i]] = k, tot -= k;
	}
}

main() {
	ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
	cin >> s, s = ' ' + s, n = s.size() - 1, tot = n * (n + 1) / 2;
	for (int i = 1; i <= n; i++)
		cin >> a[i], a[i] += a[i - 1], ans[i] = -1;
	SA(), build(1, n, 1);
	for (int i = 1; i <= n; i++) {
		if (i == 1)
			add(1, n - sa[i] + 1, 1, 1);
		else {
			int tot = qus(1, n - sa[i - 1] + 1, 1), lps = hei[i] ? qus(1, hei[i], 1) : 0;
			add(hei[i] + 1, hei[i] + 1, 1, tot - lps + 1);
			if (hei[i] < n - sa[i])
				add(hei[i] + 2, n - sa[i] + 1, 1, 1);
		}
		int l = sa[i], r = n;
		while (l ^ r) {
			int m = (l + r + 1) / 2, len = m - sa[i] + 1;
			if (tot - que(len) + 1 < a[m] - a[sa[i] - 1])
				r = m - 1;
			else
				l = m;
		}
		if (tot - que(l - sa[i] + 1) + 1 == a[l] - a[sa[i] - 1])
			ans[sa[i]] = l, res++;
	}
	cout << res << '\n';
	for (int i = 1; i <= n; i++)
		if (~ans[i])
			cout << i << ' ' << ans[i] << '\n';
}

评论

0 条评论,欢迎与作者交流。

正在加载评论...