[十二省联考 2019] 字符串问题
[十二省联考 2019] 字符串问题
题意很唬人,实际上就是让你建出一个二分图模型,然后求点权和最大的路径,其中 \(B_i\) 要向所有满足 \(B_i\) 是 \(A_j\) 的前缀的 \(A_j\) 连边。直接连边是 \(O(n^2)\) 的。考虑优化,不难想到 SA 的优良性质,即 \(B_i\) 能连边的 \(A_j\) 在 SA 中是一个区间,那么这个东西就考虑线段树优化建边,然而还有长度的限制,所以要把线段树改成主席树,一边按照长度依次加入 \(A\) 依次加入 \(B\) 连向 \(A\) 的边。点数和边数都是 \(O(n \log n)\) 的,所以总复杂度是 \(O(\sum n \log n)\)。
挺容易想的,想练一下代码能力就写了。大概写了两个半小时过了。
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
namespace IO {
#define isdigit(x) (x >= '0' && x <= '9')
template<typename T>
inline void read(T &x) {
x = 0; char ch = getchar(); int f = 0;
for(; !isdigit(ch); ch = getchar()) if(ch == '-') f = 1;
for(; isdigit(ch); ch = getchar()) x = x * 10 + ch - '0';
if(f) x = -x;
}
template<typename T, typename... Rest>
inline void read(T &x, Rest&... rest) {
read(x), read(rest...);
}
template<typename T>
inline void write(T x) {
if(x < 0) putchar('-'), x = -x;
if(x > 9) write(x / 10);
putchar(x % 10 + '0');
}
#undef isdigit
}
using namespace IO;
const int N = 2e5 + 10;
char s[N];
int n, na, nb, m;
namespace Graph {
vector<int> G[N * 40];
int w[N * 40], d[N * 40], vis[N * 40], n;
LL f[N * 40];
void clear() {
for(int i = 1; i <= n; ++i)
w[i] = d[i] = vis[i] = 0, vector<int>().swap(G[i]);
n = 0;
}
void add(int x, int y) {
if(x && y) G[x].emplace_back(y);
}
void solve() {
for(int i = 1; i <= n; ++i)
f[i] = 0;
queue<int> q;
for(int i = 1; i <= n; ++i)
for(int j : G[i])
++d[j];
for(int i = 1; i <= n; ++i)
if(d[i] == 0) q.push(i);
if(!q.size()) {
puts("-1");
return ;
}
LL ans = -1;
while(q.size()) {
int u = q.front(); q.pop();
vis[u] = 1;
f[u] += w[u];
ans = max(ans, f[u]);
for(int v : G[u]) {
f[v] = max(f[v], f[u]);
--d[v];
if(d[v] == 0)
q.push(v);
}
}
for(int i = 1; i <= n; ++i)
if(!vis[i]) {
puts("-1");
return ;
}
printf("%lld\n",ans);
}
}
namespace SA {
int sa[N], rk[N], ht[N], buc[N], id[N], ork[N];
int st[18][N];
void clear() {
for(int i = 1; i <= n; ++i)
sa[i] = rk[i] = ht[i] = buc[i] = id[i] = ork[i] = 0;
memset(st, 0, sizeof st);
}
void build() {
int m = 1 << 7, p = 0;
memset(buc, 0, (m + 1) << 2);
for(int i = 1; i <= n; ++i)
buc[rk[i] = s[i]] ++;
for(int i = 1; i <= m; ++i)
buc[i] += buc[i - 1];
for(int i = n; i >= 1; --i)
sa[buc[rk[i]]--] = i;
for(int w = 1; ; m = p, p = 0, w <<= 1) {
for(int i = n - w + 1; i <= n; ++i)
id[++p] = i;
for(int i = 1; i <= n; ++i)
if(sa[i] > w) id[++p] = sa[i] - w;
memset(buc, 0, (m + 1) << 2);
memcpy(ork, rk, (n + 1) << 2);
p = 0;
for(int i = 1; i <= n; ++i)
buc[rk[i]] ++;
for(int i = 1; i <= m; ++i)
buc[i] += buc[i - 1];
for(int i = n; i >= 1; --i)
sa[buc[rk[id[i]]]--] = id[i];
for(int i = 1; i <= n; ++i)
rk[sa[i]] = ((ork[sa[i - 1]] == ork[sa[i]] && ork[sa[i - 1] + w] == ork[sa[i] + w]) ? p : ++p);
if(p == n) break;
}
for(int i = 1, h = 0; i <= n; ++i) {
if(h) --h;
while(s[i + h] == s[sa[rk[i] - 1] + h]) ++h;
ht[rk[i]] = h;
}
for(int i = 1; i <= n; ++i)
st[0][i] = ht[i];
for(int i = 1; i <= 17; ++i)
for(int j = 1; j <= n; ++j)
if(j + (1 << (i - 1)) <= n) st[i][j] = min(st[i - 1][j], st[i - 1][j + (1 << (i - 1))]);
else st[i][j] = 0;
}
int STquery(int l, int r) {
int p = 31 - __builtin_clz(r - l + 1);
return min(st[p][l], st[p][r - (1 << p) + 1]);
}
pair<int, int> query(int x, int len) {
int lans = x, rans = x;
for(int l = 2, r = x; l <= r; ) {
int mid = (l + r) / 2;
if(STquery(mid, x) >= len) lans = mid - 1, r = mid - 1;
else l = mid + 1;
}
for(int l = x + 1, r = n; l <= r; ) {
int mid = (l + r) / 2;
if(STquery(x + 1, mid) >= len) rans = mid, l = mid + 1;
else r = mid - 1;
}
return make_pair(lans, rans);
}
}
struct SegmentTree {
struct Node {
int id;
int lc, rc;
}tr[N * 20];
#define mid (l + r) / 2
int tot;
void modify(int &k, int l, int r, int pos, int v) {
if(r < pos || pos < l) return ;
tr[++tot] = tr[k], k = tot;
if(k) Graph::add(++Graph::n, tr[k].id);
tr[k].id = Graph::n;
if(l == r) {
Graph::add(tr[k].id, v);
return ;
}
modify(tr[k].lc, l, mid, pos, v);
modify(tr[k].rc, mid + 1, r, pos, v);
if(tr[tr[k].lc].id)
Graph::add(tr[k].id, tr[tr[k].lc].id);
if(tr[tr[k].rc].id)
Graph::add(tr[k].id, tr[tr[k].rc].id);
return ;
}
void query(int k, int l, int r, int L, int R, int v) {
if(r < L || R < l || !k) return ;
if(L <= l && r <= R && tr[k].id) {
Graph::add(v, tr[k].id);
return ;
}
query(tr[k].lc, l, mid, L, R, v);
query(tr[k].rc, mid + 1, r, L, R, v);
}
#undef mid
}seg;
struct itv {
int st, len;
}a[N], b[N];
vector<int> va[N];
vector<int> vb[N];
void buildEdge() {
for(int i = 1; i <= n; ++i)
va[i].clear(), vb[i].clear();
for(int i = 1; i <= na; ++i)
va[a[i].len].emplace_back(i);
for(int i = 1; i <= nb; ++i)
vb[b[i].len].emplace_back(i);
int rt = 0;
for(int i = n; i >= 1; --i) {
for(int x : va[i])
seg.modify(rt, 1, n, SA::rk[a[x].st], x);
for(int x : vb[i]) {
pair<int, int> rng = SA::query(SA::rk[b[x].st], b[x].len);
seg.query(rt, 1, n, rng.first, rng.second, x + na);
}
}
}
void clear() {
Graph::clear();
seg.tot = 0;
}
void solve() {
scanf("%s",s + 1);
n = strlen(s + 1);
SA::build();
read(na);
for(int i = 1; i <= na; ++i) {
int l, r; read(l, r);
a[i].st = l, a[i].len = r - l + 1;
Graph::w[i] = a[i].len;
}
read(nb);
for(int i = 1; i <= nb; ++i) {
int l, r; read(l, r);
b[i].st = l, b[i].len = r - l + 1;
}
Graph::n = na + nb;
buildEdge();
read(m);
for(int i = 1; i <= m; ++i) {
int x, y; read(x, y);
Graph::add(x, y + na);
}
Graph::solve();
clear();
}
int main() {
int T; read(T);
while(T--) solve();
}