bzoj 3926 [Zjoi2015]诸神眷顾的幻想乡 后缀自动机

第一眼:树上路径,每个点的度数<=20 ? 树分治!
然而并搞不出来。发现题意是叶子数不超过20。。。

如果以每个叶节点为根建trie树,那么每个串都在这些trie的子串中出现过。深搜每个trie,每个点以父亲为last建后缀自动机。

#include <bits/stdc++.h>
using namespace std;
#define N 4100000
#define ll long long
int n,c,tot;
int a[N],head[N],nex[N],to[N],ls[N],du[N];
void add(int x,int y)
{
    tot++;
    nex[tot]=head[x];head[x]=tot;
    to[tot]=y;
}
struct SAM
{
    int trs[N][11],fa[N],len[N],cnt,last;
    void init(){last=cnt=1;}
    void insert(int x)
    {
        int p=last,np,q,nq;
        if(trs[p][x])
        {
            q=trs[p][x];
            if(len[q]==len[p]+1)np=q;
            else
            {
                fa[nq=++cnt]=fa[q];np=nq;
                len[nq]=len[p]+1;
                memcpy(trs[nq],trs[q],sizeof(trs[q]));
                fa[q]=nq;
                for(;p&&trs[p][x]==q;p=fa[p])trs[p][x]=nq;
            }
        }
        else
        {
            np=++cnt;len[np]=len[last]+1;
            for(;p&&!trs[p][x];p=fa[p])trs[p][x]=np;
            if(!p)fa[np]=1;
            else
            {
                q=trs[p][x];
                if(len[q]==len[p]+1)fa[np]=q;
                else
                {
                    fa[nq=++cnt]=fa[q];
                    len[nq]=len[p]+1;
                    memcpy(trs[nq],trs[q],sizeof(trs[q]));
                    fa[q]=fa[np]=nq;
                    for(;p&&trs[p][x]==q;p=fa[p])trs[p][x]=nq;
                }
            }
        }
        last=np;
    }
    void dfs(int x,int y)
    {
        last=ls[y];
        insert(a[x]);ls[x]=last;
        for(int i=head[x];i;i=nex[i])
            if(to[i]!=y)
                dfs(to[i],x);
    }
    ll cal()
    {
        ll ret=0;
        for(int i=1;i<=cnt;i++)
            ret+=len[i]-len[fa[i]];
        return ret;
    }
}sam;
int main()
{
    //freopen("tt.in","r",stdin);
    scanf("%d%d",&n,&c);
    for(int i=1;i<=n;i++)scanf("%d",&a[i]);
    for(int i=1,x,y;i<n;i++)
    {
        scanf("%d%d",&x,&y);
        add(x,y);add(y,x);du[x]++;du[y]++;
    }
    sam.init();ls[0]=1;
    for(int i=1;i<=n;i++)
        if(du[i]==1)sam.dfs(i,0);
    printf("%lld\n",sam.cal());
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值