后缀自动机+LCT 【bzoj4545】 DQS的Trie

来源:互联网 发布:fetch.js官网 编辑:程序博客网 时间:2024/06/04 17:46

题目大意:
维护一个树,树的边上有一个字母,支持三个操作:
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 1200000using 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;}
0 0