[BZOJ4545]DQS的trie(广义后缀自动机+lct)

版权声明:转载请注明出处:http://blog.csdn.net/clove_unique https://blog.csdn.net/Clove_unique/article/details/71188881

题目描述

传送门

题目大意:
先给出一棵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);
        }
    }
}

猜你喜欢

转载自blog.csdn.net/Clove_unique/article/details/71188881