卧薪尝胆,厚积薄发。
COCI2015 Divljak
Date: Mon Oct 08 19:24:46 CST 2018 In Category: NoCategory

Description:

有 $n$ 个字符串 $S_1,S_2,\dots,S_n$ 和一个字符串集合 $T$ ,有 $q$ 个操作,往 $T$ 集合里添加一个字符串和询问集合 $T$ 中有多少个字符串包含 $S_x$ 。
$1\leqslant n,q\leqslant 10^5,$ 字符串总长 $\leqslant 2\times 10^6$

Solution:

$T$ 集合是变动的肯定是对 $S$ 建一个 $AC$ 自动机,然后每次插入一个 $T$ 中的字符串,在 $S$ 的 $trie$ 树上从根到这个字符串对应节点这条链上所有的点在 $fail$ 树上的祖先都会答案加一,但是如果直接加会算重,所以可以用树链的并的做法,对于相邻两条从某个点到根的路径,减去他们的 $LCA$ 到根的路径,这样每个点只会被算一遍,树上路径加肯定可以树剖 $+$ 线段树或者 $LCT$ 来 $O(\log^2n)$ 或 $O(\log n)$ 做,但是会 $TLE$ ,这题有一个重要启发意义就是树上差分是可以在线的,先该怎么差分就怎么差分,然后反正树上差分的核心操作是求子树和,所以可以用一个树状数组就可以在线树上差分了。
关于到根的树链的并怎么求,可以把所有树链拿出来,按 $dfs$ 序排序,然后把所有树链加一,然后再把相邻的 $LCA$ 到根的链减一,可以证明这样就是树链的并。

Code:


#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<queue>
#include<cstring>
using namespace std;
int n,m;
#define MAXN 100010
#define MAXL 2000010
char s[MAXL];
int pos[MAXN];
struct node
{
int tr[26];
int fail;
node(){memset(tr,0,sizeof(tr));}
}t[MAXL];
int ptr = 0;
int root = 0;
int newnode(){return ++ptr;}
void insert(char c[],int id)
{
int l = strlen(c + 1);
int cur = root;
for(int i = 1;i <= l;++i)
{
if(t[cur].tr[c[i] - 'a'] == 0)
t[cur].tr[c[i] - 'a'] = newnode();
cur = t[cur].tr[c[i] - 'a'];
}
pos[id] = cur;
return;
}
void make_fail()
{
queue<int> q;
for(int i = 0;i < 26;++i)
{
if(t[root].tr[i] != 0)
{
t[t[root].tr[i]].fail = root;
q.push(t[root].tr[i]);
}
}
while(!q.empty())
{
int k = q.front();q.pop();
for(int i = 0;i < 26;++i)
{
if(t[k].tr[i] != 0)
{
t[t[k].tr[i]].fail = t[t[k].fail].tr[i];
q.push(t[k].tr[i]);
}
else
{
t[k].tr[i] = t[t[k].fail].tr[i];
}
}
}
return;
}
struct edge
{
int to,nxt;
}e[MAXL << 1];
int edgenum = 0;
int lin[MAXL] = {0};
void adde(int a,int b)
{
++edgenum;e[edgenum].to = b;e[edgenum].nxt = lin[a];lin[a] = edgenum;
++edgenum;e[edgenum].to = a;e[edgenum].nxt = lin[b];lin[b] = edgenum;
return;
}
int fa[MAXL],dep[MAXL],top[MAXL],siz[MAXL],son[MAXL],rnk[MAXL],tot = 0;
void dfs1(int k,int depth)
{
dep[k] = depth;
siz[k] = 1;
for(int i = lin[k];i != 0;i = e[i].nxt)
{
if(e[i].to != fa[k])
{
fa[e[i].to] = k;
dfs1(e[i].to,depth + 1);
siz[k] += siz[e[i].to];
if(son[k] == 0 || siz[e[i].to] > siz[son[k]])
{
son[k] = e[i].to;
}
}
}
return;
}
void dfs2(int k,int tp)
{
top[k] = tp;rnk[k] = ++tot;
if(son[k] == 0)return;
dfs2(son[k],tp);
for(int i = lin[k];i != 0;i = e[i].nxt)
{
if(e[i].to != fa[k] && e[i].to != son[k])
{
dfs2(e[i].to,e[i].to);
}
}
return;
}
int LCA(int a,int b)
{
while(top[a] != top[b])
{
if(dep[top[a]] < dep[top[b]])swap(a,b);
a = fa[top[a]];
}
return (dep[a] < dep[b] ? a : b);
}
int lowbit(int x){return x & (-x);}
int c[MAXL];
void add(int p,int k){for(int i = p;i <= tot;i += lowbit(i))c[i] += k;return;}
int query(int p){int res = 0;for(int i = p;i >= 1;i -= lowbit(i))res += c[i];return res;}
int st[MAXL],stop = 0;
bool cmp(int a,int b){return rnk[a] < rnk[b];}
void mark(char c[])
{
int l = strlen(c + 1);
int cur = root;
stop = 0;
for(int i = 1;i <= l;++i)
{
cur = t[cur].tr[c[i] - 'a'];
st[++stop] = cur;
}
sort(st + 1,st + 1 + stop,cmp);
int last = 0;
for(int i = 1;i <= stop;++i)
{
if(i == 1 || st[i] != st[i - 1])
{
add(rnk[st[i]],1);
int lca = LCA(st[i],last);
add(rnk[lca],-1);
last = st[i];
}
}
return;
}
int calc(int k)
{
int p = pos[k];
return query(rnk[p] + siz[p] - 1) - query(rnk[p] - 1);
}
int main()
{
scanf("%d",&n);
for(int i = 1;i <= n;++i)
{
scanf("%s",s + 1);
insert(s,i);
}
make_fail();
for(int i = 1;i <= ptr;++i)
{
adde(i,t[i].fail);
}
dfs1(0,0);dfs2(0,0);
scanf("%d",&m);
int opt,x;
for(int i = 1;i <= m;++i)
{
scanf("%d",&opt);
if(opt == 1)
{
scanf("%s",s + 1);
mark(s);
}
else
{
scanf("%d",&x);
printf("%d\n",calc(x));
}
}
return 0;
}
Copyright © 2020 wjh15101051
ღゝ◡╹)ノ♡