BZOJ 3926: [Zjoi2015]诸神眷顾的幻想乡(后缀自动机)

传送门

解题思路

  因为叶节点不超过(20)个,所以可以枚举这些叶节点,并把这些节点当做根扫整棵树。可以证明所有的子串一定可以被便利到,然后可以对这些串建广义后缀自动机。(dfs)的时候要记录一下上一个节点后缀自动机中的标号,从这个标号开始建后缀自动机。

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<algorithm>
 
using namespace std;
const int N=100005;
const int M=N*20;
typedef long long LL;
 
inline int rd(){
    int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)) f=ch=='-'?0:1,ch=getchar();
    while(isdigit(ch)) x=(x<<1)+(x<<3)+ch-'0',ch=getchar();
    return f?x:-x;
}
 
struct SAM{
    int fa[M<<1],ch[M<<1][11],l[M<<1],cnt;
    inline int insert(int c,int pre){
        int p=pre,np=++cnt;l[np]=l[p]+1;
        for(;p && !ch[p][c];p=fa[p]) ch[p][c]=np;
        if(!p) fa[np]=1;
        else {
            int q=ch[p][c];
            if(l[q]==l[p]+1) fa[np]=q;
            else {
                int nq=++cnt;l[nq]=l[p]+1;
                memcpy(ch[nq],ch[q],sizeof(ch[q]));
                fa[nq]=fa[q];fa[q]=fa[np]=nq;
                for(;ch[p][c]==q;p=fa[p]) ch[p][c]=nq; 
            }
        }
        return np;
    }
}sam;
 
int n,c,w[N],head[N],tot,to[N<<1],nxt[N<<1],deg[N];
LL ans;
 
inline void add(int bg,int ed){
    to[++tot]=ed,nxt[tot]=head[bg],head[bg]=tot;
}
 
void dfs(int x,int lst,int F){
    int now=sam.insert(w[x],lst);
    for(int i=head[x];i;i=nxt[i]){
        int u=to[i];if(u==F) continue;
        dfs(u,now,x);
    }
}
 
int main(){
    sam.cnt=1;n=rd(),c=rd(); int x,y;
    for(int i=1;i<=n;i++) w[i]=rd();
    for(int i=1;i<n;i++){
        x=rd(),y=rd();
        add(x,y),add(y,x);
        deg[x]++;deg[y]++;
    }
    for(int i=1;i<=n;i++)
        if(deg[i]==1) dfs(i,1,0);
    for(int i=1;i<=sam.cnt;i++) ans+=(sam.l[i]-sam.l[sam.fa[i]]);
    printf("%lld
",ans);
    return 0;
}
原文地址:https://www.cnblogs.com/sdfzsyq/p/10290195.html