【BZOJ4545】DQS的trie

Description

DQS的自家阳台上种着一棵颗粒饱满、颜色纯正的trie。
DQS的trie非常的奇特,它初始有n0个节点,n0-1条边,每条边上有一个字符。并且,它拥有极强的生长力:某个i时刻,某个节点就会新生长出一颗子树,它拥有si个节点且节点之间的边上有一个字符,并且新生长出来的子树也是一个树结构。然而因为是新长出来的,根据生活常识可知si必定不会大于i时刻之前的树的大小。
DQS定义trie的子串为从根节点(1号节点)往下走到所有节点所构成的字符串的所有的后缀。DQS身为一个单身doge,常常取出其中一个子串送给妹子,然而他并不希望送给妹子两个相同的子串,所以他非常关心当前trie的本质不同的子串数目。
DQS有时还会去商店购买子串,若他在商店看上某个子串,他希望得知这个子串是否在自家阳台的trie上已经出现,若出现则出现了多少次。如果出现了,他就可以直接回家取trie上的子串辣!
然而DQS身为一个蒟蒻,看着自家阳台的trie树一天天在长大,他被如此众多的节点弄得眼花缭乱,于是他找到了IOI2016Au的你。他会告诉你自家trie树的成长历程,他希望你能够对于每一次询问都做出正确回复。
Input

第一行输入一个整数id,代表测试点编号。
接下来一行输入一个整数n0,表示初始树的大小。
接下来n0-1行,每行两个整数u,v和一个字符c,表示u号节点和v号节点之间有一条边,边上的字母为c。
接下来输入m表示有m组操作。
对于每一组,第一行输入一个整数opt。
若opt=1,则是一组询问,询问当前trie的本质不同的子串数目是多少。
若opt=2,则后面跟两个整数rt,si,表示以点rt为根向下长出一个子树,大小为si。
接下来si-1行,每行两个整数u,v和一个字符c,表示u号节点和v号节点之间有一条边,边上的字母为c。若长出子树之前当前树的大小是n,则这si-1点的编号分别为n+1,n+2…n+si-1。
若opt=3,则是一组询问,后面输入一个字符串S,询问字符串S在当前trie中的出现次数。
Output

对于每个opt=1或3,输出一行表示答案。
Sample Input

1

4

1 2 a

1 3 b

2 4 b

6

1

2 2 4

2 5 b

2 6 c

5 7 b

1

3 ab

2 6 3

6 8 a

6 9 b

1
Sample Output

3

7

2

11

【数据范围及提示】

第一个询问,本质不同的子串是 a,b,ab。

第二个询问,本质不同的子串是 a,b,c,ab,ac,bb,abb。

第三个询问,ab出现次数是 2。

第四个询问,本质不同的子串是 a,b,c,ab,ac,ca,cb,bb,abb,aca,acb。

opt=1或3时对原树不做修改,只是询问。

每次opt=2,会增加si-1个节点,因为有一个节点是原树上作为新树的根出现的。

数据中,对于链的部分分,满足端点为根节点,每次新建子树都从尾部插入。

对于全部数据,保证从始至终每条边上的字符均为小写字母’a’或’b’或’c’。

n是最终树的大小,N<=100000,M<=100000,Si<=当前树的大小
HINT

Source

By Loi_DQS

裸SAM+LCT
抄一下2555以前写的代码
出题人强行放到trie上真是差评不已
总结一下就是这题是个SAM辣鸡题..

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define LL long long
#define MAXN 200010
#define GET (ch>='0'&&ch<='9')
#define is_root(x) (tree[tree[x].fa].ch[0]!=x&&tree[tree[x].fa].ch[1]!=x)
using namespace std;
LL ans;
int n,m,top,tp;
bool vis[MAXN];
int sta[MAXN],num[MAXN],fa[MAXN];
char s[MAXN];
inline void in(int &x)
{
    char ch=getchar();x=0;
    while (!GET)    ch=getchar();
    while (GET) x=x*10+ch-'0',ch=getchar();
}
struct Splay    {   int ch[2],fa,sum,flag;  }tree[MAXN];
struct edge {   int to,w;   edge *next; }e[MAXN<<1],*prev[MAXN];
inline void insert(int u,int v,int w)   {   e[++top].to=v;e[top].w=w;e[top].next=prev[u];prev[u]=&e[top];   }
inline void add(int x,int v)    {   if (x)  tree[x].sum+=v;tree[x].flag+=v; }
inline void push_down(int x)
{
    if (tree[x].flag)   add(tree[x].ch[0],tree[x].flag),add(tree[x].ch[1],tree[x].flag),tree[x].flag=0;
}
inline void rot(int x)
{
    int y=tree[x].fa,z=tree[y].fa,l=(tree[y].ch[1]==x),r=l^1;
    if (!is_root(y))    tree[z].ch[tree[z].ch[1]==y]=x;
    tree[tree[x].ch[r]].fa=y;tree[y].fa=x;tree[x].fa=z;
    tree[y].ch[l]=tree[x].ch[r];tree[x].ch[r]=y;
}
inline void Splay(int x)
{
    tp=0;sta[++tp]=x;
    for (int i=x;!is_root(i);i=tree[i].fa)  sta[++tp]=tree[i].fa;
    while (tp)  push_down(sta[tp--]);
    while (!is_root(x))
    {
        int y=tree[x].fa,z=tree[y].fa;
        if (!is_root(y))
        {
            if ((tree[y].ch[0]==x)^(tree[z].ch[0]==y))  rot(x);
            else    rot(y);
        }
        rot(x);
    }
}
inline void access(int x)   {   for (int i=0;x;i=x,x=tree[x].fa)    Splay(x),tree[x].ch[1]=i;   }
inline void make_root(int x)    {   access(x);Splay(x); }
inline void link(int x,int y)   {   tree[x].fa=y;make_root(y);add(y,tree[x].sum);   }
inline void cut(int x)  {   make_root(x);add(tree[x].ch[0],-tree[x].sum);tree[tree[x].ch[0]].fa=0;tree[x].ch[0]=0;  }
struct sam
{
    int p,q,np,nq,cnt;
    int a[MAXN][3],fa[MAXN],len[MAXN];
    sam()   {   ++cnt;  }
    inline int insert(int c,int last)
    {
        p=last;np=++cnt;len[np]=len[p]+1;tree[np].sum=1;
        while (!a[p][c]&&p) a[p][c]=np,p=fa[p];
        if (!p) fa[np]=1,link(np,1),ans+=len[np]-len[1];
        else
        {
            q=a[p][c];
            if (len[q]==len[p]+1)   fa[np]=q,link(np,q),ans+=len[np]-len[q];
            else
            {
                ans-=len[q]-len[fa[q]];
                nq=++cnt;len[nq]=len[p]+1;memcpy(a[nq],a[q],sizeof(a[q]));
                fa[nq]=fa[q];link(nq,fa[nq]);fa[q]=fa[np]=nq;
                cut(q);link(q,nq);link(np,nq);
                ans+=len[nq]-len[fa[nq]];ans+=len[q]-len[nq];ans+=len[np]-len[nq];
                while (a[p][c]==q)  a[p][c]=nq,p=fa[p];
            }
        }
        return np;
    }
}sam;
void dfs(int x)
{
    vis[x]=1;
    for (edge *i=prev[x];i;i=i->next)   if (!vis[i->to])    num[i->to]=sam.insert(i->w,num[x]),dfs(i->to);
}
int main()
{
    freopen("data2.in","r",stdin);freopen("data2.out","w",stdout);
    in(n);in(n);int opt,rt,sz,u,v;char c;
    for (int i=1;i<n;i++)
    {
        in(u);in(v);c=' ';while (c<'a'||c>'c')  c=getchar();
        insert(u,v,c-'a');insert(v,u,c-'a');
    }
    num[1]=1;dfs(1);
    for (in(m);m;m--)
    {
        in(opt);
        if (opt==1) printf("%I64d\n",ans);
        if (opt==2)
        {
            in(rt);in(sz);
            for (int i=1;i<sz;i++)
            {
                in(u);in(v);c=' ';while (c<'a'||c>'c')  c=getchar();
                insert(u,v,c-'a');insert(v,u,c-'a');
            }
            dfs(rt);
        }
        if (opt==3)
        {
            scanf("%s",s+1);int l=strlen(s+1),now=1;
            for (int i=1;now&&i<=l;i++) now=sam.a[now][s[i]-'a'];
            if (!now)   puts("0");
            else    Splay(now),printf("%d\n",tree[now].sum);
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值