Blog of RuSun

\begin {array}{c} \mathfrak {One Problem Is Difficult} \\\\ \mathfrak {Because You Don't Know} \\\\ \mathfrak {Why It Is Diffucult} \end {array}

P5840 [COCI2015]Divljak

P5840 [COCI2015]Divljak

$S$ 是不变的,$T$ 的动态的。考虑将所有的 $S$ 建成AC自动机,对于每一个增加的 $T$ 考虑在 $S$ 的贡献。对于 $T$ 的每一个字符,到达一个节点,所有到根节点的 $nxt$ 都是 $T$ 的子串,即在树上将一个节点到根节点的路径上点都加一。注意到问题是一个 $S$ 有多少个 $T$ 有贡献,而不是所有的 $T$ 的贡献和,即可能一个 $S$ 在 $T$ 中多次出现,只能算作一次。即匹配的若干节点到根节点的路径并的节点算贡献。考虑树上差分。将所有点按照 $dfn$ 排序,每一个节点到根节点加一,每相邻的两个节点的LCA到根节点减一;查询时查询子树权值和。将树拍成链,相当于维护区间修改,单点查询,可以用树状数组。

查看代码
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
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
#include <cstdio>
#include <queue>
#include <vector>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 2e6 + 10, M = 1e5 + 10;
char s[N];
int n, m;
vector <int> g[N];
namespace LCA
{
const int K = 25;
int d[N];
int stmp, id[N], st[N << 1][K], lg[N << 1];
void dfs (int x)
{
st[id[x] = ++stmp][0] = x;
for (int i : g[x])
{
d[i] = d[x] + 1;
dfs(i);
st[++stmp][0] = x;
}
}
int dmin (int x, int y)
{
return d[x] < d[y] ? x : y;
}
void init()
{
d[0] = 1;
dfs(0);
for (int i = 2; i <= stmp; i++)
lg[i] = lg[i >> 1] + 1;
for (int k = 1; (1 << k) <= stmp; k++)
for (int i = 1; i + (1 << k) - 1 <= stmp; i++)
st[i][k] = dmin(st[i][k - 1], st[i + (1 << k - 1)][k - 1]);
}
int lca (int x, int y)
{
x = id[x], y = id[y];
x > y && (swap(x, y), 0);
int k = lg[y - x + 1];
return dmin(st[x][k], st[y - (1 << k) + 1][k]);
}
}
namespace Trie
{
int id[M], c[N];
int stmp, dfn[N], sz[N];
int idx, cnt[N], nxt[N], tr[N][26];
namespace BIT
{
int tr[N];
void add (int x, int k)
{
for (; x <= stmp; x += x & -x)
tr[x] += k;
}
int query (int x)
{
int res = 0;
for (; x; x -= x & -x)
res += tr[x];
return res;
}
}
void insert (int x, int len, char *str)
{
int p = 0;
for (int i = 1; i <= len; i++)
{
int &t = tr[p][str[i] - 'a'];
!t && (t = ++idx);
p = t;
}
id[x] = p;
}
void build ()
{
queue <int> q;
for (int i = 0; i < 26; i++)
tr[0][i] && (q.push(tr[0][i]), 0);
while (!q.empty())
{
int t = q.front();
q.pop();
for (int i = 0; i < 26; i++)
{
int &p = tr[t][i];
if (!p)
p = tr[nxt[t]][i];
else
{
nxt[p] = tr[nxt[t]][i];
q.push(p);
}
}
g[nxt[t]].push_back(t);
}
}
void dfs (int x)
{
sz[x] = 1;
dfn[x] = ++stmp;
for (int i : g[x])
{
dfs(i);
sz[x] += sz[i];
}
}
void init()
{
scanf("%d", &n);
for (int i = 1; i <= n; i++)
{
scanf("%s", s + 1);
insert(i, strlen(s + 1), s);
}
build();
}
bool cmp (int a, int b)
{
return dfn[a] < dfn[b];
}
int main ()
{
dfs(0);
scanf("%d", &m);
for (int op; m; m--)
{
scanf("%d", &op);
if (op == 1)
{
scanf("%s", s + 1);
vector <int> q;
int len = strlen(s + 1), p = 0;
for (int i = 1; i <= len; i++)
q.push_back(p = tr[p][s[i] - 'a']);
sort(q.begin(), q.end(), cmp);
for (int i : q)
BIT::add(dfn[i], 1);
for (int i = 1; i < q.size(); i++)
BIT::add(dfn[LCA::lca(q[i], q[i - 1])], -1);
}
else if (op == 2)
{
int x;
scanf("%d", &x);
x = id[x];
printf("%d\n", BIT::query(dfn[x] + sz[x] - 1) - BIT::query(dfn[x] - 1));
}
}
return 0;
}
}
int main ()
{
Trie::init();
LCA::init();
return Trie::main();
}