写篇题解记录一下我第一道sa大题。我再也不是不会sa的sb了
其实自从去年12月份决定放弃冲省队开始就一直没有按原计划继续学下去(废话),所以各种后缀数据结构一片空白,结果在考场上遇到了这个题。当时我就知道这题肯定是用后缀数据结构优化匹配,然后用线段树优化建图,但是我并不能写得出任何一个可用的后缀数据结构…
其实不是什么很难的东西啦…只是代码有点长。
首先考虑一个40分暴力:对于每一组支配关系 ( x , y ) (x,y) (x,y),从 A x A_x Ax向 B y B_y By连边;用字符串哈希判断如果 B i B_i Bi是 A j A_j Aj的前缀,从 B i B_i Bi向 A j A_j Aj连边。这两类边分别是 O ( m ) O(m) O(m)和 O ( n a n b ) O(n_an_b) O(nanb)的。然后跑DAG最长路即可。
这里的复杂度主要来源于第二类数量为 O ( n a n b ) O(n_an_b) O(nanb)的边。我们求出后缀数组以及 h e i g h t \mathrm{height} height数组,那么对于一个 B i = ( l b i , r b i ) B_i=(\mathrm{lb}_i,\mathrm{rb}_i) Bi=(lbi,rbi),可以找到最小的 l l l和最大的 r r r,满足 l c p { s a l , s a l + 1 , ⋯   , s a r } ≥ r b i − l b i + 1 \mathrm{lcp}\{\mathrm{sa}_l,\mathrm{sa}_{l+1},\cdots,\mathrm{sa}_r\}\geq \mathrm{rb}_i-\mathrm{lb}_i+1 lcp{sal,sal+1,⋯,sar}≥rbi−lbi+1,那么所有满足 r a n k l a j ∈ [ l , r ] \mathrm{rank}_{\mathrm{la}_j}\in[l,r] ranklaj∈[l,r]的 A A A类串 A j A_j Aj都包含 B i B_i Bi作为前缀,因此我们从 B i B_i Bi向区间 [ l , r ] [l,r] [l,r]连边。这个连边可以用线段树优化。
可是这样写只有 80 80 80分。问题在于,这样做就没有考虑到 ∣ A j ∣ < ∣ B i ∣ |A_j|<|B_i| ∣Aj∣<∣Bi∣的情况。我们可以按 A A A类串的长度从大到小建立可持久化线段树,在对应的树上连边。这个过程我借鉴了一下小粉兔大佬的写法,将所有的 A A A串 B B B串混在一起按长度排序,长度相同的让 A A A串在前,这样的话直接扫一遍,碰到 A A A串就建新树,碰到 B B B串就在当前树上连边即可。
然后DAG最长路的求法多种多样。我一开始的naive做法是先判环,没环的话再跑记忆化搜索;然而判环我又一直写不对,于是我就直接上了Tarjan,然后常数瞬间变大。。。不过幸好时限够长没有什么问题。事实上可以直接在记忆化搜索的时候判环,就像NOIP2017day1t3那样写。
#include <cctype>
#include <cstdio>
#include <climits>
#include <algorithm>
#include <cstring>
#include <vector>
template <typename T> inline void read(T& x) {
int f = 0, c = getchar(); x = 0;
while (!isdigit(c)) f |= c == '-', c = getchar();
while (isdigit(c)) x = x * 10 + c - 48, c = getchar();
if (f) x = -x;
}
template <typename T, typename... Args>
inline void read(T& x, Args&... args) {
read(x); read(args...);
}
template <typename T> void write(T x) {
if (x < 0) x = -x, putchar('-');
if (x > 9) write(x / 10);
putchar(x % 10 + 48);
}
template <typename T> inline void writeln(T x) { write(x); puts(""); }
template <typename T> inline bool chkmin(T& x, const T& y) { return y < x ? (x = y, true) : false; }
template <typename T> inline bool chkmax(T& x, const T& y) { return x < y ? (x = y, true) : false; }
typedef long long LL;
const int maxn = 2e5 + 207;
char s[maxn];
int sa[maxn], rank[maxn], height[maxn];
int tmp[maxn], tax[maxn];
int st[30][maxn], lg[maxn];
int la[maxn], ra[maxn], lb[maxn], rb[maxn];
int kase, n, na, nb, m, sigma;
struct Sub {
int rk, len, pos;
bool tp;
Sub() : rk(0), len(0), pos(0), tp(0) {}
Sub(int r, int l, int p, bool t) : rk(r), len(l), pos(p), tp(t) {}
};
Sub sub[maxn << 1];
inline bool operator<(const Sub &lhs, const Sub &rhs) {
return lhs.len == rhs.len ? (int)lhs.tp < rhs.tp : lhs.len > rhs.len;
}
inline void rsort() {
std::fill(tax, tax + sigma + 1, 0);
for (int i = 1; i <= n; ++i) ++tax[rank[i]];
for (int i = 1; i <= sigma; ++i) tax[i] += tax[i - 1];
for (int i = n; i; --i) sa[tax[rank[tmp[i]]]--] = tmp[i];
}
inline void getsa() {
sigma = 26;
for (int i = 1; i <= n; ++i)
rank[i] = s[i] - 'a' + 1, tmp[i] = i;
rsort();
for (int w = 1, p = 0; p < n; sigma = p, w <<= 1) {
int cnt = 0;
for (int i = 1; i <= w; ++i) tmp[++cnt] = n - w + i;
for (int i = 1; i <= n; ++i) if (sa[i] > w) tmp[++cnt] = sa[i] - w;
rsort(); std::swap(tmp, rank);
rank[sa[1]] = p = 1;
for (int i = 2; i <= n; ++i)
rank[sa[i]] = tmp[sa[i]] == tmp[sa[i - 1]] && tmp[sa[i] + w] == tmp[sa[i - 1] + w] ? p : ++p;
}
}
inline void getheight() {
for (int i = 1, k = 0; i <= n; ++i) {
if (rank[i] == 1) { height[rank[i]] = k = 0; continue; }
if (k) --k;
int j = sa[rank[i] - 1];
while (i + k <= n && j + k <= n && s[i + k] == s[j + k]) ++k;
height[rank[i]] = k;
}
for (int i = 2; i <= n; ++i) {
lg[i] = lg[i >> 1] + 1;
st[0][i] = height[i];
}
for (int j = 1; 1 << j <= n; ++j) {
for (int i = 1; i <= 1 << j; ++i) st[j][i] = 0;
for (int i = 1 << j | 1; i <= n; ++i)
st[j][i] = std::min(st[j - 1][i], st[j - 1][i - (1 << (j - 1))]);
}
}
std::vector<int> G[maxn << 5];
inline void ae(int x, int y) { G[x].push_back(y); }
LL f[maxn << 5];
bool vis[maxn << 5], in[maxn << 5];
LL dfs(int x) {
if (in[x]) return -1;
if (vis[x]) return f[x];
in[x] = 1;
LL self = x > na ? 0 : ra[x] - la[x] + 1;
f[x] = self;
for (auto v : G[x]) {
LL ret = dfs(v);
if (ret == -1) return -1;
chkmax(f[x], self + ret);
}
in[x] = 0;
vis[x] = 1;
return f[x];
}
struct Node {
int lc, rc;
Node() : lc(0), rc(0) {}
};
Node T[maxn << 5];
int root[maxn], tot;
inline void clear() {
for (int i = 1; i <= tot; i += 4) {
T[i].lc = 0; T[i + 1].lc = 0; T[i + 2].lc = 0; T[i + 3].lc = 0;
T[i].rc = 0; T[i + 1].rc = 0; T[i + 2].rc = 0; T[i + 3].rc = 0;
}
for (int i = 1; i <= tot; i += 4) {
f[i] = 0; f[i + 1] = 0; f[i + 2] = 0; f[i + 3] = 0;
vis[i] = 0; vis[i + 1] = 0; vis[i + 2] = 0; vis[i + 3] = 0;
in[i] = 0; in[i + 1] = 0; in[i + 2] = 0; in[i + 3] = 0;
}
for (int i = 1; i <= tot; i += 4) {
G[i].clear(); G[i + 1].clear(); G[i + 2].clear(); G[i + 3].clear();
}
for (int i = 1; i <= na; i += 4) {
root[i] = 0; root[i + 1] = 0; root[i + 2] = 0; root[i + 3] = 0;
}
}
void modify(int &o, int l, int r, int pos, int a) {
T[++tot] = T[o];
if (o) ae(tot, o);
o = tot;
if (l == r) { ae(o, a); return; }
int mid = (l + r) >> 1;
if (pos <= mid) {
modify(T[o].lc, l, mid, pos, a);
ae(o, T[o].lc);
} else {
modify(T[o].rc, mid + 1, r, pos, a);
ae(o, T[o].rc);
}
}
void addedge(int o, int lb, int rb, int u, int l, int r) {
if (!o || l > rb || r < lb) return;
if (l <= lb && r >= rb) { ae(u, o); return; }
int mid = (lb + rb) >> 1;
addedge(T[o].lc, lb, mid, u, l, r);
addedge(T[o].rc, mid + 1, rb, u, l, r);
}
int main() {
read(kase);
while (kase--) {
scanf("%s", s + 1); n = strlen(s + 1);
getsa(); getheight();
read(na);
for (int i = 1; i <= na; ++i) {
read(la[i], ra[i]);
sub[i] = Sub(rank[la[i]], ra[i] - la[i] + 1, i, 0);
}
read(nb);
for (int i = 1; i <= nb; ++i) {
read(lb[i], rb[i]);
sub[na + i] = Sub(rank[lb[i]], rb[i] - lb[i] + 1, i, 1);
}
std::sort(sub + 1, sub + na + nb + 1);
tot = na + nb;
for (int i = 1, o = 0; i <= na + nb; ++i) {
if (sub[i].tp) {
int lb = sub[i].rk, rb = sub[i].rk;
for (int j = lg[sub[i].rk - 1]; ~j; --j)
if (st[j][lb] >= sub[i].len) lb -= 1 << j;
for (int j = lg[n - sub[i].rk]; ~j; --j)
if (rb + (1 << j) <= n && st[j][rb + (1 << j)] >= sub[i].len) rb += 1 << j;
addedge(root[o], 1, n, na + sub[i].pos, lb, rb);
} else {
++o;
modify(root[o] = root[o - 1], 1, n, sub[i].rk, sub[i].pos);
}
}
read(m);
for (int i = 1, x, y; i <= m; ++i)
read(x, y), ae(x, na + y);
bool circle = 0;
for (int i = 1; i <= tot && !circle; ++i)
if (!vis[i]) if (dfs(i) == -1) circle = 1;
if (circle) puts("-1");
else writeln(*std::max_element(f + 1, f + tot + 1));
clear();
}
return 0;
}