2754: [SCOI2012]喵星球上的点名
Time Limit: 20 Sec Memory Limit: 128 MB
Submit: 3171 Solved: 1481
[Submit][Status][Discuss]
Description
a180285幸运地被选做了地球到喵星球的留学生。他发现喵星人在上课前的点名现象非常有趣。 假设课堂上有N个喵星人,每个喵星人的名字由姓和名构成。喵星球上的老师会选择M个串来点名,每次读出一个串的时候,如果这个串是一个喵星人的姓或名的子串,那么这个喵星人就必须答到。 然而,由于喵星人的字码过于古怪,以至于不能用ASCII码来表示。为了方便描述,a180285决定用数串来表示喵星人的名字。
现在你能帮助a180285统计每次点名的时候有多少喵星人答到,以及M次点名结束后每个喵星人答到多少次吗?
Input
现在定义喵星球上的字符串给定方法:
先给出一个正整数L,表示字符串的长度,接下来L个整数表示字符串的每个字符。
输入的第一行是两个整数N和M。
接下来有N行,每行包含第i 个喵星人的姓和名两个串。姓和名都是标准的喵星球上的
字符串。
接下来有M行,每行包含一个喵星球上的字符串,表示老师点名的串。
Output
对于每个老师点名的串输出有多少个喵星人应该答到。
然后在最后一行输出每个喵星人被点到多少次。
Sample Input
2 3
6 8 25 0 24 14 8 6 18 0 10 20 24 0
7 14 17 8 7 0 17 0 5 8 25 0 24 0
4 8 25 0 24
4 7 0 17 0
4 17 0 8 25
Sample Output
2
1
0
1 2
【提示】
事实上样例给出的数据如果翻译成地球上的语言可以这样来看
2 3
izayoi sakuya
orihara izaya
izay
hara
raiz
HINT
【数据范围】
对于30%的数据,保证:
1<=N,M<=1000,喵星人的名字总长不超过4000,点名串的总长不超过2000。
对于100%的数据,保证:
1<=N<=20000,1<=M<=50000,喵星人的名字总长和点名串的总长分别不超过100000,保证喵星人的字符串中作为字符存在的数不超过10000。
分析:考虑后缀数组的做法。
首先把所有名字和询问都串起来建一个后缀数组,那么对于每个询问,可以左右二分一下lcp>=len,找出所有该串出现的位置,那么对于每个询问就相当于是询问一个区间,第一问是求这个区间内有多少种不同的数字,第二问是求每种数字被多少个不同的区间覆盖过。
第一问:把询问离线,预处理出每个数下一次出现的pos,开始询问之前,把所有不同的数第一次出现的地方都+1,当目前询问到的区间的左端点超过了某个点,就需要把这个点的nxt位置+1,然后直接查询该区间的和就可以了,这样可以保证每个点在同一个区间内只被计算一次。
第二问:同样把询问离线,再预处理处每个数上一次出现的pos,从第一个点遍历到最后一个点,对于一个区间[L,R],当遍历到L的时候,在L处+1,当遍历到R+1时,再在L处-1,然后ans[a[i]]就加上a[i]上一次出现的位置+1到i的区间和。
区间和直接用bit维护就可以了。
#include "bits/stdc++.h"
using namespace std;
const int maxn = 5e5;
int belong[maxn], pos[maxn], len[maxn], ans1[maxn], ans2[maxn], now[maxn], nxt[maxn], las[maxn];
pair<pair<int, int>, int> q[maxn];
vector<int> add[maxn], del[maxn];
struct BIT {
int n;
vector<int> v;
BIT(int n) : n(n) { v.resize(n + 1); }
void init(int n) {
v.resize(n + 1);
for (int i = 0; i <= n; ++i)v[i] = 0;
}
void update(int x, int d) {
while (x <= n)v[x] += d, x += (x & -x);
}
int que(int x) {
int res = 0;
while (x > 0)res += v[x], x -= (x & -x);
return res;
}
} bit(maxn);
struct hasaki {
int n, m, s[maxn], sa[maxn], x[maxn], y[maxn], c[maxn], height[maxn], rk[maxn], dp[maxn][40];
void Suffix() {
for (int i = 0; i < m; i++)c[i] = 0;
for (int i = 0; i < n; i++)c[x[i] = s[i]]++;
for (int i = 0; i < m; i++)c[i] += c[i - 1];
for (int i = n - 1; i >= 0; i--)sa[--c[x[i]]] = i;
for (int k = 1; k <= n; k <<= 1) {
int p = 0;
for (int i = n - k; i < n; i++)y[p++] = i;
for (int i = 0; i < n; i++) if (sa[i] >= k) y[p++] = sa[i] - k;
for (int i = 0; i < m; i++) c[i] = 0;
for (int i = 0; i < n; i++) c[x[y[i]]]++;
for (int i = 0; i < m; i++) c[i] += c[i - 1];
for (int i = n - 1; i >= 0; i--) sa[--c[x[y[i]]]] = y[i];
swap(x, y);
p = 1;
x[sa[0]] = 0;
for (int i = 1; i < n; i++)
x[sa[i]] = y[sa[i]] == y[sa[i - 1]] && y[sa[i] + k] == y[sa[i - 1] + k] ? p - 1 : p++;
if (p >= n) break;
m = p;
}
}
void getheight() {
for (int i = 0; i < n; i++) rk[sa[i]] = i;
for (int i = 0, k = 0; i < n; i++) {
if (rk[i]) {
if (k) --k;
else k = 0;
int j = sa[rk[i] - 1];
while (s[i + k] == s[j + k])k++;
height[rk[i]] = k;
}
}
}
void init() {
for (int i = 0; i < n; ++i)dp[i][0] = height[i];
for (int j = 1; (1 << j) < n; ++j) {
for (int i = 0; i + (1 << j) - 1 < n; ++i) {
dp[i][j] = 0x3f3f3f3f;
dp[i][j] = min(dp[i][j - 1], dp[i + (1 << j - 1)][j - 1]);
}
}
}
int que(int l, int r) {
if (l > r)swap(l, r);
l++;
int k = log2(r - l + 1);
return min(dp[l][k], dp[r - (1 << k) + 1][k]);
}
} shit;
int main() {
int n, m, x, l;
scanf("%d%d", &n, &m);
for (int i = 0; i < n; ++i) {
scanf("%d", &l);
while (l--)scanf("%d", &x), belong[shit.n] = i + 1, shit.s[shit.n++] = x;
shit.s[shit.n++] = 1e4 + 1;
scanf("%d", &l);
while (l--)scanf("%d", &x), belong[shit.n] = i + 1, shit.s[shit.n++] = x;
shit.s[shit.n++] = 1e4 + 1;
}
for (int i = 0; i < m; ++i) {
scanf("%d", &l), len[i] = l, pos[i] = shit.n;
while (l--)scanf("%d", &x), shit.s[shit.n++] = x;
shit.s[shit.n++] = 1e4 + 1;
}
shit.m = 10002, shit.Suffix(), shit.getheight(), shit.init();
for (int i = 0; i < m; ++i) {
int l, r, ansl = shit.rk[pos[i]], ansr = shit.rk[pos[i]];
l = 0, r = ansl - 1;
while (l <= r) {
int mid = l + r >> 1;
if (shit.que(mid, shit.rk[pos[i]]) >= len[i]) {
ansl = mid;
r = mid - 1;
} else l = mid + 1;
}
l = ansr + 1, r = shit.n - 1;
while (l <= r) {
int mid = l + r >> 1;
if (shit.que(mid, shit.rk[pos[i]]) >= len[i]) {
ansr = mid;
l = mid + 1;
} else r = mid - 1;
}
q[i] = make_pair(make_pair(ansl, ansr), i);
}
sort(q, q + m);
memset(now, -1, sizeof(now));
memset(nxt, -1, sizeof(nxt));
memset(las, -1, sizeof(las));
for (int i = shit.n - 1; i >= 0; --i) {
int x = belong[shit.sa[i]];
if (x == 0)continue;
if (now[x] != -1)nxt[i + 1] = now[x];
now[x] = i + 1;
}
memset(now, -1, sizeof(now));
for (int i = 0; i < shit.n; ++i) {
x = belong[shit.sa[i]];
if (x == 0)continue;
if (now[x] != -1)las[i + 1] = now[x];
else bit.update(i + 1, 1);
now[x] = i;
}
int laast = 0;
for (int i = 0; i < m; ++i) {
add[q[i].first.first + 1].push_back(1);
del[q[i].first.second + 2].push_back(q[i].first.first + 1);
while (laast <= q[i].first.first) {
if (nxt[laast] != -1)bit.update(nxt[laast], 1);
laast++;
}
ans1[q[i].second] = bit.que(q[i].first.second + 1) - bit.que(q[i].first.first);
}
bit.init(maxn);
for (int i = 1; i <= shit.n; ++i) {
bit.update(i, add[i].size());
for (int j = 0; j < del[i].size(); ++j)bit.update(del[i][j], -1);
int last = max(las[i] + 1, 0);
ans2[belong[shit.sa[i - 1]]] += bit.que(i) - bit.que(last);
}
for (int i = 0; i < m; ++i)printf("%d\n", ans1[i]);
for (int i = 1; i <= n; ++i)printf("%d ", ans2[i]);
}