BZOJ 3926 Zjoi2015 诸神眷顾的幻想乡 后缀自动机

题目大意:给定一棵树,每个节点有一个字符,求从一个节点出发沿最短路径走到另一个节点所构成的字符串一共有多少种

此生无悔入东方,来世愿生幻想乡

题目戳这里

注意一句话:太阳花田的结构比较特殊,只与一个空地相邻的空地的数量不超过20个

有奖问答:↑你看到这句话的第一反应是啥?

1.度数<=20

2.叶节点数<=20

仔细看几遍就能找到答案~

[捂脸熊]陈老师真是语文高手。。。。

叶节点数<=20还做啥了。。。

直接从每个叶节点DFS一遍,然后构建广义后缀自动机,最终答案就是每个节点的深度-parent节点的深度之和

还有一种后缀数组的做法是DFS20遍构建一棵Trie树,然后将Trie树建成后缀数组(OTZ),然后一样搞就是了

OTZ

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define M 100100
using namespace std;
struct abcd{
    int to,next;
}table[M<<1];
int head[M],tot;
int n,c,a[M];
long long ans;
int degree[M];
void Add(int x,int y)
{
    table[++tot].to=y;
    table[tot].next=head[x];
    head[x]=tot;
}
namespace Suffix_Automaton{
    struct Sam{
        Sam *son[10],*parent;
        int max_dpt;
        bool v;
        void* operator new (size_t,int _)
        {
            #define L (1<<15)
            static Sam *mempool,*C;
            if(C==mempool)
                mempool=(C=new Sam[L])+L;
            C->max_dpt=_;
            C->parent=0x0;
            C->v=false;
            memset(C->son,0,sizeof C->son);
            return C++;
        }
    }*root=new (0)Sam;
    Sam* Extend(Sam *p,int x)
    {
        if(p->son[x]&&p->son[x]->max_dpt==p->max_dpt+1)
            return p->son[x];
        Sam *np=new (p->max_dpt+1)Sam;
        while(p&&!p->son[x])
            p->son[x]=np,p=p->parent;
        if(!p)
            np->parent=root;
        else
        {
            Sam *q=p->son[x];
            if(p->max_dpt+1==q->max_dpt)
                np->parent=q;
            else
            {
                Sam *nq=new (p->max_dpt+1)Sam;
                nq->parent=q->parent;
                memcpy(nq->son,q->son,sizeof nq->son);
                q->parent=nq;np->parent=nq;
                for(;p&&p->son[x]==q;p=p->parent)
                    p->son[x]=nq;
            }
        }
        return np;
    }
}
using namespace Suffix_Automaton;
Sam *node[M];
void DFS(int x,int from)
{
    int i;
    node[x]=Extend(node[from],a[x]);
    for(i=head[x];i;i=table[i].next)
        if(table[i].to!=from)
            DFS(table[i].to,x);
}
void BFS()
{
    static Sam *q[4004004];
    int i,r=0,h=0;
    q[++r]=root;
    while(r!=h)
    {
        Sam *p=q[++h];
        if(p!=root)
            ans+=p->max_dpt-p->parent->max_dpt;
        for(i=0;i<c;i++)
            if(p->son[i]&&!p->son[i]->v)
                p->son[i]->v=true,q[++r]=p->son[i];
    }
}
int main()
{
    int i,x,y;
    cin>>n>>c;
    for(i=1;i<=n;i++)
        scanf("%d",&a[i]);
    for(i=1;i<n;i++)
    {
        scanf("%d%d",&x,&y);
        Add(x,y);Add(y,x);
        degree[x]++;
        degree[y]++;
    }
    node[0]=root;
    for(i=1;i<=n;i++)
        if(degree[i]==1)
            DFS(i,0);
    BFS();
    cout<<ans<<endl;
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值