【bzoj3926】[Zjoi2015]诸神眷顾的幻想乡

7 篇文章 0 订阅
1 篇文章 0 订阅

首先一个串的不同子串和就是sam所有的st[i]-st[fail[i]]之和。
这题以每个叶子结点为根,dfs建立广义sam就好了。。(其实学过sam还是蛮简单的吧吧吧)
(去年写的,代码似乎不忍直视?)

#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
#define ll long long
#define gc getchar()
#define N 100009
#define M 4000009
using namespace std;
int num=0,first[N],n,m,v[N],du[N];
ll ans;
struct edge
{
    int next,to;
    void add(int x,int y)
    {
        to=y,next=first[x],first[x]=num;
    }
}e[N*2];
struct sam
{
    int fail[M],st[M],son[M][10],cnt;
    void csh()
    {
        cnt=1;
    }
    int extend(int p,int c)
    {
        int np=++cnt;
        st[np]=st[p]+1;
        while (!son[p][c]&&p) son[p][c]=np,p=fail[p];
        if (!p) fail[np]=1;
        else
        {
            int q=son[p][c];
            if (st[q]==st[p]+1)
                fail[np]=q;
            else
            {
                int nq=++cnt;
                st[nq]=st[p]+1;
                memcpy(son[nq],son[q],sizeof(son[q]));
                fail[nq]=fail[q];
                fail[q]=fail[np]=nq;
                while (son[p][c]==q) son[p][c]=nq,p=fail[p];
            }
        }
        return np;
    }
    void work()
    {
        for (int i=1;i<=cnt;i++)
            ans+=st[i]-st[fail[i]];//st[i]-st[fail[i]] -> different substring number
    }
}Sam;
int read()
{
    int x=1;
    char ch;
    while (ch=gc,ch<'0'||ch>'9')
        if (ch=='-') x=-1;
    int s=ch-48;
    while (ch=gc,ch>='0'&&ch<='9')
        s=s*10+ch-48;
    return s*x;
}
void dfs(int now,int father,int p)
{
    int temp=Sam.extend(p,v[now]);
    for (int i=first[now];i;i=e[i].next)
        if (e[i].to!=father)
            dfs(e[i].to,now,temp);
}
int main()
{
    Sam.csh();
    n=read(),m=read();
    for (int i=1;i<=n;i++) v[i]=read();
    for (int i=1;i<n;i++)
    {
        int x=read(),y=read();
        e[++num].add(x,y),e[++num].add(y,x);
        du[x]++,du[y]++;
    }
    for (int i=1;i<=n;i++)
        if (du[i]==1) dfs(i,0,1);//leaf node -> build eneralized sam
    Sam.work();
    printf("%lld\n",ans);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值