题目地址
BZOJ2754: [SCOI2012]喵星球上的点名
题解
做法:后缀数组+ST表+树状数组。
先把所有串拼在一起,处理出SA数组,然后分别考虑两个问题。
对于第一个问题,首先对于每个询问,能够对答案有贡献的串一定在连续的一个区间且包含询问串(在经过后缀排序之后)。因为\(\text {LCP(x,y)} = \min_{k=x+1}^y\{ \text{height[k]}\}\),所以可以先预处理一下ST表,然后二分找出对每个询问有贡献的区间,即\(\{ k|\text{LCP(k,i)}\geq \text{length}_k \}\)。
那么现在的问题就转变成了一个非常经典的问题,给出\(m\)个询问,每次询问区间\([l,r]\)的颜色数。
这个问题直接离线树状数组即可。(将询问按右端点排序,然后处理出\(pre\)数组表示当前位置的数上一次出现的位置,每次在树状数组中加入当前数的同时删掉该数的\(pre\),就可以正确得到区间颜色数了)。
对于第二个问题,和第一个问题本质基本相同,转化为求每个点被多少个询问区间覆盖。这个东西差分+离线+树状数组即可解决。
总复杂度是\(O(n \log n)\)的。当然这题的做法很多,有将问题转化后\(O(n \sqrt n)\)莫队求解的,也有直接大力上AC自动机的。不过目前我会的只有后缀数组相关做法...
#include
using namespace std;
const int N = 500010;
int a[N], n, m, len, bl[N], T = 10001, length[N], st[N], L[N];
int sa[N], height[N], tong[N], rnk[N], tp[N], ans[N], pre[N];
struct Q {int l, r, id;} q[N];
bool cmp(Q a, Q b) {return a.r < b.r;}
void radix_sort() {
for(int i = 1; i <= m; ++i) tong[i] = 0;
for(int i = 1; i <= len; ++i) tong[rnk[i]]++;
for(int i = 1; i <= m; ++i) tong[i] += tong[i - 1];
for(int i = len; i; --i) sa[tong[rnk[tp[i]]]--] = tp[i];
}
void suffix_sort() {
for(int i = 1; i <= len; ++i) rnk[i] = a[i], tp[i] = i;
m = T; radix_sort();
for(int w = 1, p = 1; p < len && w <= len; w <<= 1, m = p) {
p = 0;
for(int i = 1; i <= w; ++i) tp[++p] = len - w + i;
for(int i = 1; i <= len; ++i) if(sa[i] > w) tp[++p] = sa[i] - w;
radix_sort(); swap(tp, rnk); rnk[sa[1]] = p = 1;
for(int i = 2; i <= len; ++i)
rnk[sa[i]] = (tp[sa[i]] == tp[sa[i - 1]] && tp[sa[i] + w] == tp[sa[i - 1] + w]) ? p : ++p;
}
for(int i = 1, k = 0; i <= len; ++i) {
if(k) --k;
int j = sa[rnk[i] - 1];
while(a[i + k] == a[j + k] && i + k <= len && j + k <= len) ++k;
height[rnk[i]] = k;
}
}
namespace ST {
int f[N][25], LG[N];
int query(int l, int r) {
int k = LG[r - l + 1];
return min(f[l][k], f[r - (1 << k) + 1][k]);
}
void init() {
for(int i = 1; i <= len; ++i) f[i][0] = height[i];
for(int i = 2; i <= len; ++i) LG[i] = LG[i >> 1] + 1;
for(int j = 1; j <= 24; ++j)
for(int i = 1; i + (1 << j) - 1 <= len; ++i)
f[i][j] = min(f[i][j - 1], f[i + (1 << (j - 1))][j - 1]);
}
}
namespace BIT {
int c[N];
#define lowbit(i) (i & -i)
void clear() {memset(c, 0, sizeof(c));}
void add(int x, int v) {
if(!x) return;
for(int i = x; i <= len; i += lowbit(i)) c[i] += v;
}
int query(int x) {
if(!x) return 0;
int ans = 0;
for(int i = x; i; i -= lowbit(i)) ans += c[i];
return ans;
}
}
int main() {
#ifndef ONLINE_JUDGE
freopen("1.in", "r", stdin);
freopen("qwq.out", "w", stdout);
#endif
int M; scanf("%d%d", &n, &M);
for(int i = 1; i <= n; ++i) {
int x, tmp; scanf("%d", &x);
while(x--) {
scanf("%d", &tmp);
a[++len] = tmp; bl[len] = i;
}
a[++len] = ++T;
scanf("%d", &x);
while(x--) {
scanf("%d", &tmp);
a[++len] = tmp; bl[len] = i;
}
a[++len] = ++T;
}
for(int i = 1; i <= M; ++i) {
int x, tmp; scanf("%d", &x);
length[i] = x;
st[i] = len + 1;
while(x--) {
scanf("%d", &tmp);
a[++len] = tmp;
}
a[++len] = ++T;
}
suffix_sort(); ST::init();
for(int i = 1; i <= M; ++i) {
int l = rnk[st[i]], r = len;
q[i].l = q[i].r = rnk[st[i]];
while(l <= r) {
int mid = (l + r) >> 1;
if(ST::query(rnk[st[i]] + 1, mid) >= length[i]) l = mid + 1, q[i].r = mid;
else r = mid - 1;
}
l = 1, r = rnk[st[i]];
while(l <= r) {
int mid = (l + r) >> 1;
if(ST::query(mid + 1, rnk[st[i]]) >= length[i]) r = mid - 1, q[i].l = mid;
else l = mid + 1;
}
q[i].id = i;
}
memset(tong, 0, sizeof(tong));
for(int i = 1; i <= len; ++i) {
if(bl[sa[i]] > 0) {
pre[i] = tong[bl[sa[i]]];
tong[bl[sa[i]]] = i;
}
}
sort(q + 1, q + M + 1, cmp);
int cur = 1; BIT::clear();
for(int i = 1; i <= len; ++i) {
if(bl[sa[i]]) {BIT::add(pre[i], -1); BIT::add(i, 1);}
while(cur <= M && q[cur].r == i) {
ans[q[cur].id] = BIT::query(q[cur].r) - BIT::query(q[cur].l - 1);
++cur;
}
}
for(int i = 1; i <= M; ++i) printf("%d\n", ans[i]);
memset(ans, 0, sizeof(ans));
for(int i = 1; i <= M; ++i) L[i] = q[i].l;
sort(L + 1, L + M + 1); cur = 1; int k = 1;
BIT::clear();
for(int i = 1; i <= len; ++i) {
while(cur <= M && L[cur] == i) BIT::add(i, 1), ++cur;
if(bl[sa[i]] > 0) ans[bl[sa[i]]] += BIT::query(i) - BIT::query(pre[i]);
while(k <= M && q[k].r == i) BIT::add(q[k++].l, -1);
}
for(int i = 1; i <= n; ++i) printf("%d ", ans[i]); puts("");
return 0;
}