bzoj 2754 [SCOI2012]喵星球上的點名 字尾陣列+莫隊
阿新 • • 發佈:2018-12-22
題面
解法
之前曾嘗試用AC自動機暴力水過去,然而T了……
- AC自動機顯然是可以實現的,但是因為字符集太大,所以會導致超時。
- 那麼我們考慮字尾陣列解決。首先我們可以將所有串整個拼成一個新的字串。名和姓之間用一種分隔符隔開,不同的字串之間用另一種分隔符隔開。
- 然後構造出字尾陣列。考慮如何求解第一問,顯然可以在後綴陣列中找到以當前字串開頭的字尾的位置,然後上下二分一下對應的區間滿足排序後區間對應的字尾的字首都為當前詢問的字串。然後就變成求解一個區間中有多少個不同的數的問題了,可以使用樹狀陣列。此處使用莫隊,因為這樣方便求解第二問。
- 然後我們考慮如何求解第二問。在莫隊的時候,對於新增和刪除分別處理。我們可以記錄當前這個數上一次出現的位置在哪裡。如果這個數在當前詢問的區間中被刪掉了,那麼更新這個數的答案。如果這個數是第一次被新增進這個區間,那麼更新這個數上一次出現的位置。
- 時間複雜度:
程式碼
#include <bits/stdc++.h>
#define N 300010
using namespace std;
template <typename T> void read(T &x) {
x = 0; int f = 1; char c = getchar();
while (!isdigit(c)) {if (c == '-') f = -1; c = getchar();}
while (isdigit(c)) x = x * 10 + c - '0', c = getchar(); x *= f;
}
struct Node {int l, r, id;} a[N];
int n, m, sum, c[N], s[N], y[N], sa[N], st[N], ans[N], las[N], len[N], num[N], pos[N], rnk[N], f[N][21];
bool cmp (Node a, Node b) {return (a.l >> 9) == (b.l >> 9) ? a.r < b.r : a.l < b.l;}
void del(int x, int y) {s[c[x]]--; if (!s[c[x]]) sum--, num[c[x]] += y - las[c[x]];}
void ins(int x, int y) {if (!s[c[x]]) sum++, las[c[x]] = y; s[c[x]]++;}
int lcp(int i, int j) {
int l = i + 1, r = j, t = log2(r - l + 1);
return min(f[l][t], f[r - (1 << t) + 1][t]);
}
void Sort() {
for (int i = 1; i <= m; i++) s[i] = 0;
for (int i = 1; i <= n; i++) s[rnk[i]]++;
for (int i = 1; i <= m; i++) s[i] += s[i - 1];
for (int i = n; i; i--) sa[s[rnk[y[i]]]--] = y[i];
}
void build() {
for (int i = 1; i <= n; i++) rnk[i] = st[i], y[i] = i;
m = 1e4 + 10, Sort(); int len = 0;
for (int k = 1; k <= n; k <<= 1, m = len, len = 0) {
for (int i = n - k + 1; i <= n; i++) y[++len] = i;
for (int i = 1; i <= n; i++) if (sa[i] > k) y[++len] = sa[i] - k;
Sort(), swap(rnk, y), rnk[sa[1]] = len = 1;
for (int i = 2; i <= n; i++)
rnk[sa[i]] = y[sa[i - 1]] == y[sa[i]] && y[sa[i - 1] + k] == y[sa[i] + k] ? len : ++len;
if (len >= n) break;
}
for (int i = 1, k = 0; i <= n; i++) {
if (rnk[i] == 1) continue;
if (k) k--; int j = sa[rnk[i] - 1];
while (st[i + k] == st[j + k]) k++;
f[rnk[i]][0] = k;
}
for (int j = 1; (1 << j) <= n; j++)
for (int i = 1; i + (1 << j) - 1 <= n; i++)
f[i][j] = min(f[i][j - 1], f[i + (1 << j - 1)][j - 1]);
}
int main() {
int tot, q; read(tot), read(q);
for (int i = 1; i <= tot; i++) {
int l; read(l);
while (l--) read(st[++n]);
st[++n] = 1e4 + 1, read(l);
while (l--) read(st[++n]);
st[++n] = 1e4 + 2;
}
int tmp = n, cnt = 1;
for (int i = 1; i <= q; i++) {
int l; read(l);
pos[i] = n + 1, len[i] = l;
while (l--) read(st[++n]);
if (i != q) st[++n] = 1e4 + 2;
}
build();
for (int i = 1; i <= tmp; i++) if (st[i] == 1e4 + 2) cnt++; else c[rnk[i]] = cnt;
for (int i = 1; i <= q; i++) {
int x = rnk[pos[i]], L = len[i];
int l = 1, r = x - 1, tl = x;
while (l <= r) {
int mid = (l + r) >> 1;
if (lcp(mid, x) >= L) tl = mid, r = mid - 1;
else l = mid + 1;
}
l = x + 1, r = n; int tr = x;
while (l <= r) {
int mid = (l + r) >> 1;
if (lcp(x, mid) >= L) tr = mid, l = mid + 1;
else r = mid - 1;
}
a[i] = (Node) {tl, tr, i};
}
sort(a + 1, a + q + 1, cmp);
memset(s, 0, sizeof(s)), sum = 0;
for (int i = 1, l = 1, r = 0; i <= q; i++) {
while (r < a[i].r) ins(++r, i);
while (r > a[i].r) del(r--, i);
while (l < a[i].l) del(l++, i);
while (l > a[i].l) ins(--l, i);
ans[a[i].id] = sum - (s[0] > 0);
}
for (int i = 1; i <= q; i++) cout << ans[i] << "\n";
for (int i = 1; i <= tot; i++) if (s[i]) num[i] += q - las[i] + 1;
for (int i = 1; i <= tot; i++) cout << num[i] << ' '; cout << "\n";
return 0;
}