【CF#163E】E-Government

  • AC自动机重修系列第三题

昨晚写了阿狸的打字机之后,这道题就是套路了

题目大意:

要求你写出一个网络市政系统,用于评定每篇文章的“政治程度”。每篇文章的“政治程度”的含义就是文章中出现了多少个参议员的名字。这里要注意,参议员的名字可能是交叉的。 这个系统还需支持三种操作:

1、将某个参议员的名字从名单中抹去(不再考虑此议员)

2、将某个已被删除的议员的名字重新加入名单。

3、输出给定的文章的“政治程度”。

题解

一般统计串的出现次数,比较朴素的做法就是在AC自动机上跑到每个节点时沿着fail指针计算答案

但是在这里会TLE,我们考虑优化

我们可以建出fail树,求出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
67
68
69
70
71
72
#include<bits/stdc++.h>
#define FILE "read"
#define MAXN 1000010
using namespace std;
struct node{int y,next;}e[MAXN<<1];
int n,m,ans,cnt,dfs_clock,len,pos[MAXN],vis[MAXN],fail[MAXN],Link[MAXN],q[MAXN],L[MAXN],R[MAXN],c[MAXN<<1],tr[MAXN][27];
char ch[MAXN];
inline int read(){
int x=0,f=1; char ch=getchar();
while(!isdigit(ch)) {if(ch=='-') f=-1; ch=getchar();}
while(isdigit(ch)) {x=x*10+ch-'0'; ch=getchar();}
return x*f;
}
void insert(int x,int y){e[++len].next=Link[x];Link[x]=len;e[len].y=y;}
void add(int x,int v){while(x<=dfs_clock)c[x]+=v,x+=(x&-x);}
int get(int x){int temp(0);while(x)temp+=c[x],x-=(x&-x);return temp;}
void dfs(int x){
L[x]=++dfs_clock;
for(int i=Link[x];i;i=e[i].next)
dfs(e[i].y);
R[x]=++dfs_clock;
}
void extend(int &now){
scanf("%s",ch+1); int N=strlen(ch+1);
for(int i=1;i<=N;++i){
if(!tr[now][ch[i]-'a']) tr[now][ch[i]-'a']=++cnt;
now=tr[now][ch[i]-'a'];
}
}
void build(){
int head=0,tail=0;
for(int i=0;i<26;++i) if(tr[0][i]) q[++tail]=tr[0][i];
while(++head<=tail){
int x=q[head];
for(int i=0;i<26;++i){
if(!tr[x][i]) tr[x][i]=tr[fail[x]][i];
else fail[tr[x][i]]=tr[fail[x]][i],q[++tail]=tr[x][i];
}
}
for(int i=1;i<=cnt;++i) insert(fail[i],i); dfs(0);
}
void work(){
scanf("%s",ch+1); int now=0,N=strlen(ch+1);
for(int i=1;i<=N;++i){
while(now&&!tr[now][ch[i]-'a']) now=fail[now];
now=tr[now][ch[i]-'a']; ans+=get(L[now]);
}
}
void solve(){
n=read(); m=read();
for(int i=1;i<=m;++i) extend(pos[i]); build();
for(int i=1;i<=m;++i) vis[i]=1;
for(int i=1;i<=m;++i) add(L[pos[i]],1),add(R[pos[i]],-1);
for(int i=1;i<=n;++i){
char opt; scanf("\n%c",&opt);
if(opt=='?') ans=0,work(),printf("%d\n",ans);
else if(opt=='-'){
int x=read();if(!vis[x])continue;
add(L[pos[x]],-1);add(R[pos[x]],1);vis[x]=0;
}
else{
int x=read();if(vis[x]) continue;
add(L[pos[x]],1);add(R[pos[x]],-1);vis[x]=1;
}
}
}
int main(){
freopen(FILE".in","r",stdin);
freopen(FILE".out","w",stdout);
solve();
return 0;
}
文章目录
  1. 1. 题目大意:
  2. 2. 题解
,