标签:线段 getch out 区间 ++ err oid codeforce back
https://codeforces.com/problemset/problem/163/E
题意:给 \(n\) 个串,初始每个串都是有效的,\(m\) 次操作,能让某个串无效,或者某个串重新生效,或者求有效串在新给的 \(s\) 中的总出现次数。
题解:
考虑询问 \(s\) 中出现了多少个模式串,建立ac自动机,对fail树上每个模式串末尾点权值+1,那么 \(s\) 的答案就是在trie上跑,每个点在fail树上到根的链的权值和。
问题变成:给一棵树,单点点权修改,查询点到根的path权值和。
这个的话按dfs序编号,考虑对 \(x\) 单点修改相当于把 \(x\) 所在子树中所有点的答案都增加了 1,用差分树状数组做或者线段树区间加都可。
代码:
/*================================================================
*
* 创 建 者: badcw
* 创建日期: 2020/6/21 5:07
*
================================================================*/
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn = 1e6+50;
const int mod = 1e9+7;
ll qp(ll a, ll n, ll mod = ::mod) {
ll res = 1;
while (n > 0) {
if (n & 1) res = res * a % mod;
a = a * a % mod;
n >>= 1;
}
return res;
}
int tot;
int pos[maxn], out[maxn];
vector<int> edge[maxn];
void dfs(int u) {
pos[u] = ++tot;
for (auto v : edge[u]) dfs(v);
out[u] = tot;
}
struct AC {
#define M 26
#define fi ‘a‘
#define tp char
#define lst 0
int trie[maxn][M], sz, fa[maxn];
int fail[maxn], deg[maxn];
int insert(tp *s) {
int rt = 0;
for (int i = 0; s[i] != lst; ++i) {
int nxt = s[i] - fi;
if (!trie[rt][nxt]) trie[rt][nxt] = ++sz;
fa[trie[rt][nxt]] = rt;
rt = trie[rt][nxt];
}
return rt;
}
void build() {
queue<int> qu;
for (int i = 0; i < M; ++i) if (trie[0][i]) {
fail[trie[0][i]] = 0, qu.push(trie[0][i]);
edge[0].push_back(trie[0][i]);
}
while (!qu.empty()) {
int k = qu.front();
qu.pop();
for (int i = 0; i < M; ++i) {
if (trie[k][i]) {
fail[trie[k][i]] = trie[fail[k]][i];
edge[trie[fail[k]][i]].push_back(trie[k][i]);
qu.push(trie[k][i]);
deg[fail[trie[k][i]]] ++;
} else trie[k][i] = trie[fail[k]][i];
}
}
}
}ac;
int n, m;
int id[maxn], inv[maxn], vis[maxn];
char s[maxn];
int pre[maxn];
void add(int pos, int k) { while (pos <= tot) pre[pos] += k, pos += pos & -pos; }
int query(int pos) {
int res = 0;
while (pos > 0) {
res += pre[pos];
pos -= pos & -pos;
}
return res;
}
int main(int argc, char* argv[]) {
scanf("%d%d", &m, &n);
for (int i = 1; i <= n; ++i) {
scanf("%s", s);
id[i] = ac.insert(s);
inv[id[i]] = i;
}
ac.build();
dfs(0);
for (int i = 1; i <= n; ++i) {
// cerr << pos[id[i]] << " " << out[id[i]] << endl;
add(pos[id[i]], 1);
vis[i] = 1;
add(out[id[i]] + 1, -1);
}
while (m--) {
char t = getchar();
while (t == ‘ ‘ || t == ‘\n‘) t = getchar();
if (t == ‘+‘) {
int x;
scanf("%d", &x);
if (vis[x] == 1) continue;
vis[x] = 1;
add(pos[id[x]], 1);
add(out[id[x]] + 1, - 1);
} else if (t == ‘-‘) {
int x;
scanf("%d", &x);
if (vis[x] == 0) continue;
vis[x] = 0;
add(out[id[x]] + 1, 1);
add(pos[id[x]], -1);
} else {
scanf("%s", s);
int rt = 0;
int res = 0;
for (int i = 0; s[i]; ++i) {
int nxt = s[i] - ‘a‘;
rt = ac.trie[rt][nxt];
// if (inv[rt]) {
// cerr << "add " << pos[rt] << endl;
res += query(pos[rt]);
// }
// cerr << rt << " " << res << endl;
}
printf("%d\n", res);
}
}
return 0;
}
标签:线段 getch out 区间 ++ err oid codeforce back
原文地址:https://www.cnblogs.com/badcw/p/13171281.html