只要你跑的够快,锅就追不上你

0%

「十二省联考 2019」字符串问题(后缀数组 + 线段树)

题目大意

「十二省联考 2019」字符串问题(Luogu 5284)

题太长了,自己读吧 QAQ。

令 $n = \vert S \vert$,则:

  • 对于 $40 \%$ 的数据,$n_a \times n_b \le 2 \times 10^5$。
  • 对于 $80 \%$ 的数据,$\vert A_i \vert \ge \vert B_i \vert$。
  • 对于 $100 \%$ 的数据,$n, n_a, n_b, m \le 2 \times 10^5$。

思路分析

算法一

直接借助 Hash 暴力建图,做拓扑排序后 DP 求最长链即可。

时间复杂度 $O(n + n_a \times n_b + m)$,期望得分 $40$ 分。

算法二

可以发现复杂度的瓶颈在于 “如果 $B_j$ 是 $A_i$ 的前缀,则从 $B_j$ 向 $A_i$ 连一条边” 这一部分。如果有 $\vert A_i \vert \ge \vert B_i \vert$,那么连边的条件等价于:

对于串后缀排序,则 $B_j$ 对应的 $A$ 就会形成一段区间。这段区间可以通过在 $\text{height}$ 数组上倍增来找到。找到区间以后,我们使用线段树优化建图即可。

我们将 $n, n_a, n_b, m$ 看作同阶,时间复杂度 $O(n \log n)$,期望得分 $80$ 分。

算法三

如果不保证 $\vert A_i \vert \ge \vert B_i \vert$,那么连边的条件就多了一个,也就是增加了一维的限制。我们使用主席树来代替线段树优化建边即可。

我们将 $n, n_a, n_b, m$ 看作同阶,时间复杂度 $O(n \log n)$,期望得分 $100$ 分。

代码实现

1
#include <cstdio>
2
#include <cstring>
3
#include <queue>
4
#include <vector>
5
#include <algorithm>
6
using namespace std;
7
8
#define mid ((l + r) / 2)
9
typedef long long ll;
10
11
struct event {
12
	int type, id, x, y;
13
	event(int type = 0, int id = 0, int x = 0, int y = 0): type(type), id(id), x(x), y(y) {}
14
	friend bool operator< (const event &a, const event &b) {
15
		return a.x == b.x ? a.type < b.type : a.x > b.x;
16
	}
17
};
18
19
const int maxn = 2e5, maxm = 2 * maxn, maxv = 5e6, logn = 17;
20
int T, len, sa[maxn + 3], rnk[maxn + 3], _cnt[maxn + 3], a[maxn + 3], b[maxn + 3], hei[logn + 3][maxn + 3];
21
int n, m, k, lg, l1[maxn + 3], ln1[maxn + 3], l2[maxn + 3], ln2[maxn + 3], deg[maxv + 3], tot, cnt, rt, ch[maxv + 3][2];
22
char s[maxn + 3];
23
event e[maxm + 3];
24
queue<int> Q;
25
vector<int> G[maxv + 3];
26
ll dp[maxv + 3];
27
28
void radix_sort(int a[], int b[], int k[], int n) {
29
	fill(_cnt + 1, _cnt + n + 1, 0);
30
	for (int i = 1; i <= n; i++) _cnt[k[i]]++;
31
	for (int i = 2; i <= n; i++) _cnt[i] += _cnt[i - 1];
32
	for (int i = n; i; i--) b[_cnt[k[a[i]]]--] = a[i];
33
}
34
35
void suffix_sort(int n) {
36
	for (int i = 1; i <= n; i++) _cnt[s[i] - 'a' + 1] = 1;
37
	for (int i = 2; i <= 26; i++) _cnt[i] += _cnt[i - 1];
38
	for (int i = 1; i <= n; i++) rnk[i] = _cnt[s[i] - 'a' + 1];
39
	if (_cnt[26] == n) { for (int i = 1; i <= n; i++) sa[rnk[i]] = i; return; }
40
	for (int k = 1; k < n; k *= 2) {
41
		for (int i = 1; i <= n; i++) {
42
			a[i] = rnk[i], b[i] = (i + k <= n ? rnk[i + k] : 0) + 1;
43
		}
44
		for (int i = 1; i <= n; i++) sa[i] = i;
45
		radix_sort(sa, rnk, b, n), radix_sort(rnk, sa, a, n);
46
		rnk[sa[1]] = 1;
47
		for (int i = 2; i <= n; i++) {
48
			rnk[sa[i]] = rnk[sa[i - 1]] + (a[sa[i]] != a[sa[i - 1]] || b[sa[i]] != b[sa[i - 1]]);
49
		}
50
		if (rnk[sa[n]] == n) return;
51
	}
52
}
53
54
void get_height(int n) {
55
	for (int i = 1, j, t = 0; i <= n; hei[0][rnk[i++] - 1] = t) {
56
		for (t = max(0, t - 1), j = sa[rnk[i] - 1]; s[i + t] == s[j + t]; t++);
57
	}
58
}
59
60
void add_edge(int u, int v) {
61
	G[u].push_back(v), deg[v]++;
62
}
63
64
65
int build(int l, int r) {
66
	int x = ++tot;
67
	if (l == r) return ch[x][0] = 0, ch[x][1] = 0, x;
68
	return ch[x][0] = build(l, mid), ch[x][1] = build(mid + 1, r), x;
69
}
70
71
int grow(int bs, int l, int r, int y, int z) {
72
	int x = ++tot;
73
	ch[x][0] = ch[bs][0], ch[x][1] = ch[bs][1], add_edge(x, bs);
74
	if (l == r) return add_edge(x, y), x;
75
	if (z <= mid) ch[x][0] = grow(ch[bs][0], l, mid, y, z), add_edge(x, ch[x][0]);
76
	else ch[x][1] = grow(ch[bs][1], mid + 1, r, y, z), add_edge(x, ch[x][1]);
77
	return x;
78
}
79
80
void solve(int x, int l, int r, int lx, int rx, int y) {
81
	if (!x) return;
82
	if (l >= lx && r <= rx) return add_edge(y, x), void();
83
	if (lx <= mid) solve(ch[x][0], l, mid, lx, rx, y);
84
	if (rx > mid) solve(ch[x][1], mid + 1, r, lx, rx, y);
85
}
86
87
int main() {
88
	scanf("%d", &T);
89
	while (T--) {
90
		scanf("%s", s + 1), len = strlen(s + 1);
91
		suffix_sort(len), get_height(len);
92
		lg = 0;
93
		for (int k = 1; 1 << k <= len - 1; k++) {
94
			lg = k;
95
			for (int i = 1, j = (1 << (k - 1)) + 1; i <= len - (1 << k); i++, j++) {
96
				hei[k][i] = min(hei[k - 1][i], hei[k - 1][j]);
97
			}
98
		}
99
		cnt = 0;
100
		scanf("%d", &n);
101
		for (int i = 1; i <= n; i++) {
102
			scanf("%d %d", &l1[i], &ln1[i]), ln1[i] = ln1[i] - l1[i] + 1;
103
			e[++cnt] = event(1, i, ln1[i], l1[i]);
104
		}
105
		scanf("%d", &m);
106
		for (int i = 1; i <= m; i++) {
107
			scanf("%d %d", &l2[i], &ln2[i]), ln2[i] = ln2[i] - l2[i] + 1;
108
			e[++cnt] = event(2, i, ln2[i], l2[i]);
109
		}
110
		sort(e + 1, e + cnt + 1);
111
		scanf("%d", &k);
112
		for (int i = 1, x, y; i <= k; i++) {
113
			scanf("%d %d", &x, &y), add_edge(x, y + n);
114
		}
115
		tot = n + m, rt = 0;
116
		rt = build(1, len);
117
		for (int i = 1; i <= cnt; i++) {
118
			if (e[i].type == 1) {
119
				rt = grow(rt, 1, len, e[i].id, rnk[e[i].y]);
120
			} else {
121
				int l = rnk[e[i].y], r = l;
122
				for (int k = lg; ~k; k--) {
123
					if (l - (1 << k) > 0 && hei[k][l - (1 << k)] >= e[i].x) l -= 1 << k;
124
				}
125
				for (int k = lg; ~k; k--) {
126
					if (r + (1 << k) <= len && hei[k][r] >= e[i].x) r += 1 << k;
127
				}
128
				solve(rt, 1, len, l, r, e[i].id + n);
129
			}
130
		}
131
		for (int i = 1; i <= tot; i++) {
132
			dp[i] = i <= n ? ln1[i] : 0;
133
			if (!deg[i]) Q.push(i);
134
		}
135
		cnt = 0;
136
		ll ans = 0;
137
		while (!Q.empty()) {
138
			int u = Q.front();
139
			cnt++, Q.pop(), ans = max(ans, dp[u]);
140
			for (int i = 0, v; i < G[u].size(); i++) {
141
				v = G[u][i];
142
				dp[v] = max(dp[v], dp[u] + (v <= n ? ln1[v] : 0));
143
				if (!--deg[v]) Q.push(v);
144
			}
145
		}
146
		if (cnt != tot) puts("-1");
147
		else printf("%lld\n", ans);
148
		for (int i = 1; i <= tot; i++) {
149
			G[i].clear(), deg[i] = 0;
150
		}
151
	}
152
	return 0;
153
}