题目描述
题目大意:
先给出一棵trie,然后支持几种操作
若opt=1,则是一组询问,询问当前trie的本质不同的子串数目是多少。
若opt=2,则后面跟两个整数rt,si,表示以点rt为根向下长出一个子树,大小为si。即加入一个子trie
若opt=3,则是一组询问,后面输入一个字符串S,询问字符串S在当前trie中的出现次数。
题解
这题其实是substring和生成魔咒的结合版
opt=1其实就是对于每一个主链上的点,求sigma step(i)-step(pre(i)),每一次extend的时候算一下就行了
opt=3是将S子在自动机上匹配之后求匹配到的点right集合的大小,也就是pa树上子树的大小,用lct维护一下
代码
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
using namespace std;
#define N 400005
int T,n,m,mak,ans3;long long ans1;
int mark[N],father[N],last[N];char s[N];
int tot,point[N],nxt[N],v[N];char c[N];
int root,sz,p,np,q,nq,pre[N],step[N],son[N][5];
int f[N],ch[N][2],size[N],delta[N],stack[N];
//----------------------------------lct--------------------------------------
bool isroot(int x)
{
return ch[f[x]][0]!=x&&ch[f[x]][1]!=x;
}
int get(int x)
{
return ch[f[x]][1]==x;
}
void pushdown(int x)
{
if (x&&delta[x])
{
int l=ch[x][0],r=ch[x][1];
if (l) size[l]+=delta[x],delta[l]+=delta[x];
if (r) size[r]+=delta[x],delta[r]+=delta[x];
delta[x]=0;
}
}
void rotate(int x)
{
int old=f[x],oldf=f[old],wh=get(x);
if (!isroot(old)) ch[oldf][ch[oldf][1]==old]=x;
f[x]=oldf;
ch[old][wh]=ch[x][wh^1];
if (ch[old][wh]) f[ch[old][wh]]=old;
ch[x][wh^1]=old;
f[old]=x;
}
void splay(int x)
{
int top=0;stack[++top]=x;
for (int i=x;!isroot(i);i=f[i]) stack[++top]=f[i];
for (int i=top;i;--i) pushdown(stack[i]);
for (int fa;!isroot(x);rotate(x))
if (!isroot(fa=f[x]))
rotate(get(x)==get(fa)?fa:x);
}
void access(int x)
{
int t=0;
for (;x;t=x,x=f[x])
{
splay(x);
ch[x][1]=t;
}
}
void link(int x,int y)
{
f[y]=x;
access(x);
splay(x);
size[x]+=size[y];
delta[x]+=size[y];
}
void cut(int x,int y)
{
access(y);
splay(y);
size[x]-=size[y];
delta[x]-=size[y];
ch[y][0]=f[x]=0;
}
//----------------------------------sam--------------------------------------
void extend(int p,int x)
{
np=++sz;
step[np]=step[p]+1;
while (p&&!son[p][x])
{
son[p][x]=np;
p=pre[p];
}
if (!p)
{
pre[np]=root;
link(root,np);
}
else
{
q=son[p][x];
if (step[q]==step[p]+1)
{
pre[np]=q;
link(pre[np],np);
}
else
{
nq=++sz;
step[nq]=step[p]+1;
pre[nq]=pre[q];
link(pre[nq],nq);
memcpy(son[nq],son[q],sizeof(son[q]));
while (p&&son[p][x]==q)
{
son[p][x]=nq;
p=pre[p];
}
cut(pre[q],q);
pre[np]=pre[q]=nq;
link(pre[q],q);
link(pre[np],np);
}
}
ans1+=(long long)step[np]-(long long)step[pre[np]];
access(np);
splay(np);
++size[np];
++delta[np];
}
int sam()
{
int len=strlen(s);p=root;
for (int i=0;i<len;++i)
{
int x=s[i]-'a';
p=son[p][x];
}
if (!p) return 0;
access(p);
splay(p);
return size[p];
}
//----------------------------------trie-------------------------------------
void add(int x,int y,int z)
{
++tot; nxt[tot]=point[x]; point[x]=tot; v[tot]=y; c[tot]=z;
}
void dfs(int x,int fa,int mak)
{
father[x]=fa;
for (int i=point[x];i;i=nxt[i])
if (v[i]!=fa&&mark[v[i]]==mak)
{
extend(last[x],c[i]);
last[v[i]]=np;
dfs(v[i],x,mak);
}
}
//----------------------------------main-------------------------------------
int main()
{
scanf("%d",&T);
scanf("%d",&n);
root=++sz;last[1]=root;
++mak;
for (int i=1;i<n;++i)
{
int x,y;char z;scanf("%d%d %c",&x,&y,&z);
add(x,y,z-'a'),add(y,x,z-'a');mark[x]=mark[y]=mak;
}
dfs(1,0,mak);
scanf("%d",&m);
while (m--)
{
int opt;scanf("%d",&opt);
if (opt==1) printf("%lld\n",ans1);
else if (opt==2)
{
int ri,si;scanf("%d%d",&ri,&si);
++mak;
for (int i=1;i<si;++i)
{
int x,y;char z;scanf("%d%d %c",&x,&y,&z);
add(x,y,z-'a'),add(y,x,z-'a');mark[x]=mark[y]=mak;
}
dfs(ri,father[ri],mak);
}
else if (opt==3)
{
scanf("%s",s);
ans3=sam();
printf("%d\n",ans3);
}
}
}