題目大意:
維護一個樹,樹的邊上有一個字母,支持三個操作:
1、查詢本質不同的子串的個數;
2、在樹上的某一個節點長出一個新的子樹;
3、查詢某個串出現的次數。
題目分析:
對這棵樹維護一個廣義的後綴自動機。
對於1操作,只要維護每個節點的max_ len減去parent樹上父節點的max_len,詳見bzoj4516生成魔咒的題解;
對於3操作,則維護LCT進行鏈加即可,詳見bzoj2555 SubString的題解。
所以說這題是4516和2555二合一啊O。O
注意:1操作要開long long
代碼如下:
#include <cstdio>
#include <cstring>
#define N 1200000
using namespace std;
int id,n,m,opt,x,y,rt,sz;
long long ans;
char s[N],q[N<<1];
int fir[N],nes[N<<1],v[N<<1],fa[N],tot=1,last_tot=1;
void edge(int x,int y,char c)
{
v[++tot]=y;
q[tot]=c;
nes[tot]=fir[x];
fir[x]=tot;
return;
}
#define edge(x,y,c) edge(x,y,c),edge(y,x,c)
struct splay{
splay *ch[2],*fa;
int sum,mark;
splay();
void add_mark(int);
void push_down();
void push_up();
int dir();
}*null=new splay;
splay :: splay()
{
sum=mark=0;
ch[1]=ch[0]=fa=null;
}
void splay :: add_mark(int v)
{
if(this==null) return;
sum+=v;
mark+=v;
}
void splay :: push_down()
{
if(mark)
{
ch[0]->add_mark(mark);
ch[1]->add_mark(mark);
mark=0;
}
}
void splay :: push_up()
{
if(~dir()) fa->push_up();
push_down();
}
int splay :: dir()
{
return fa->ch[0]==this?0:fa->ch[1]==this?1:-1;
}
void turn(splay *c,int d)
{
splay *y=c->ch[d^1];
c->ch[d^1]=y->ch[d];
if(y->ch[d]!=null) y->ch[d]->fa=c;
y->ch[d]=c;
y->fa=c->fa;
int k;
if(~(k=c->dir())) c->fa->ch[k]=y;
c->fa=y;
}
void splaying(splay *c)
{
c->push_up();
int d;
while(~(d=c->dir()))
{
if(d==c->fa->dir()) turn(c->fa->fa,d^1);
turn(c->fa,d^1);
}
}
void Access(splay *c)
{
splay *y=null;
while(c!=null)
{
splaying(c);
c->ch[1]=y;
y=c;
c=c->fa;
}
return;
}
void Cut(splay *x)
{
Access(x),splaying(x);
x->ch[0]->fa=null;
x->ch[0]=null;
}
void Link(splay *x,splay *y)
{
Cut(x);
x->fa=y;
}
struct SAM{
SAM *son[26],*fa;
int max_len;
splay *tree;
SAM(int _):max_len(_)
{
memset(son,0,sizeof(son));
fa=NULL;
tree=new splay;
}
}*last[N],*root=new SAM(0);
SAM* extend(SAM *last,int x)
{
SAM *p=last;
SAM *np=new SAM(p->max_len+1);
while(p && !p->son[x]) p->son[x]=np,p=p->fa;
if(!p) np->fa=root;
else
{
SAM *q=p->son[x];
if(q->max_len==p->max_len+1) np->fa=q;
else
{
SAM *nq=new SAM(p->max_len+1);
ans-=q->max_len-q->fa->max_len;
q->tree->push_up();
nq->tree->sum=q->tree->sum;
Link(q->tree,nq->tree);
Link(nq->tree,q->fa->tree);
nq->fa=q->fa;
np->fa=nq; q->fa=nq;
ans+=q->max_len-q->fa->max_len;
ans+=nq->max_len-nq->fa->max_len;
memcpy(nq->son,q->son,sizeof(nq->son));
for(;p && p->son[x]==q;p=p->fa) p->son[x]=nq;
}
}
ans+=np->max_len-np->fa->max_len;
Link(np->tree,np->fa->tree);
Access(np->tree),splaying(np->tree);
np->tree->add_mark(1);
return np;
}
void dfs(int c)
{
for(int t=fir[c];t;t=nes[t])
{
if(v[t]==fa[c]) continue;
if(t<=last_tot) break;
last[v[t]]=extend(last[c],q[t]-'a');
fa[v[t]]=c;
dfs(v[t]);
}
return;
}
int query(char *s)
{
SAM *c=root;
for(int i=0;s[i];i++)
{
if(!c->son[s[i]-'a']) return 0;
c=c->son[s[i]-'a'];
}
c->tree->push_up();
return c->tree->sum;
}
int main()
{
scanf("%d",&id);
scanf("%d",&n);
for(int i=1;i<n;i++)
{
scanf("%d%d%s",&x,&y,s);
edge(x,y,s[0]);
}
last[1]=root;
dfs(1);
scanf("%d",&m);
while(m--)
{
scanf("%d",&opt);
switch(opt)
{
case 1:
printf("%lld\n",ans);
break;
case 2:
scanf("%d%d",&rt,&sz);
last_tot=tot;
for(int i=1;i<sz;i++)
{
scanf("%d%d%s",&x,&y,s);
edge(x,y,s[0]);
}
dfs(rt);
break;
case 3:
scanf("%s",s);
printf("%d\n",query(s));
break;
}
}
return 0;
}