「BZOJ-3926」[Zjoi2015]诸神眷顾的幻想乡(后缀自动机)

BZOJ3122-[Zjoi2015]诸神眷顾的幻想乡

题解

写了cls的题当然要写题解辣!

题意相当于给了你一棵点数为$n$的树,每个点上有一个(a-j)的字符,求这颗树上所有本质不同字符串的个数(一条路径上起点与终点相反的两个串视为不同)。

由于所给的树叶子节点不超过20个,考虑以每一个叶子节点为起点进行dfs,建立广义后缀自动机,即在父节点的状态后加入子节点,然后求本质不同的字符串个数即可。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
#include <bits/stdc++.h>

using namespace std;
typedef long long ll;

const int maxn = 2e6 + 10;

int col[maxn], in[maxn];
vector<int> G[maxn];

int last = 1, cnt = 1;
int ch[maxn << 1][10], fa[maxn << 1], len[maxn << 1], pos[maxn];
int sz[maxn << 1], a[maxn << 1], c[maxn << 1];

int ins(int c)
{
int p = last, np = ++ cnt;
last = np, len[np] = len[p] + 1;
for(; p && !ch[p][c]; p = fa[p]) ch[p][c] = np;
if(!p) fa[np] = 1;
else
{
int q = ch[p][c];
if(len[p] + 1 == len[q]) fa[np] = q;
else
{
int nq = ++ cnt;
len[nq] = len[p] + 1;
memcpy(ch[nq], ch[q], sizeof ch[q]);
fa[nq] = fa[q], fa[q] = fa[np] = nq;
for(; ch[p][c] == q; p = fa[p]) ch[p][c] = nq;
}
}
return np;
}

void dfs(int u, int pre, int p)
{
last = p;
int q = ins(col[u]);
for(int i = 0; i < G[u].size(); i ++)
{
int v = G[u][i];
if(v == pre) continue;
dfs(v, u, q);
}
}

int main()
{
int n, c;
scanf("%d%d", &n, &c);
for(int i = 1; i <= n; i ++) scanf("%d", &col[i]);
for(int i = 1; i < n; i ++)
{
int u, v;
scanf("%d%d", &u, &v);
G[u].push_back(v), G[v].push_back(u);
in[u] ++; in[v] ++;
}
for(int i = 1; i <= n; i ++) if(in[i] == 1) dfs(i, 0, 1);
long long ans = 0;
for(int i = 1; i <= cnt; i ++) ans += len[i] - len[fa[i]];
printf("%lld\n", ans);
return 0;
}