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

%%%

给定一棵叶子不超过20的树,点有颜色,每两点间的路径构成一个颜色串,所有的串不同的有多少种。

神犇们都太强了!

首先以每个叶子为根,就得到了一个Trie树啊。

Trie树怎么建到SAM上呢,直接以Trie上的父亲节点在SAM中的位置作为$last$,和字符串一样加入建边更新就行了。

怎么把所有20个Trie树建到一个SAM上呢?每个Trie树直接从$init$状态开始建就行了。

这就是广义后缀自动机?为啥对呢,,感觉就非常对2333

#include<bits/stdc++.h>
using namespace std;
const int N=100010;
inline int read(){
    int r=0,c=getchar();
    while(!isdigit(c))c=getchar();
    while(isdigit(c))
    r=r*10+c-'0',c=getchar();
    return r;
}
struct SAM{
    int ch[N*20][11],fa[N*20],len[N*20];
    int sz;
    void init(){
        sz=0;fa[0]=-1;
    }
    int ins(int c,int las){
        int now=++sz;len[now]=len[las]+1;
        int p,q;
        for(p=las;~p&&!ch[p][c];p=fa[p])
        ch[p][c]=now;
        if(!~p)fa[now]=0;
        else{
            q=ch[p][c];
            if(len[q]==len[p]+1)
                fa[now]=q;
            else{
                int r=++sz;
                fa[r]=fa[q];
                for(int i=0;i<10;i++)
                ch[r][i]=ch[q][i];
                len[r]=len[p]+1;
                for(;~p&&ch[p][c]==q;p=fa[p])
                ch[p][c]=r;
                fa[q]=fa[now]=r;
            }
        }
        return now;
    }
}atm;
struct Edge{
    int to,nxt;
}e[N*2];
int head[N],cnt=1;
int d[N],col[N];
void add(int u,int v){
    e[cnt]=(Edge){v,head[u]};
    head[u]=cnt++;
    e[cnt]=(Edge){u,head[v]};
    head[v]=cnt++;
    d[u]++,d[v]++;
}
void dfs(int u,int fa,int las){
    int now=atm.ins(col[u],las);
    for(int i=head[u];i;i=e[i].nxt)
    if(e[i].to^fa)dfs(e[i].to,u,now);
}
int main(){
    int n=read();read();
    for(int i=1;i<=n;i++)
    col[i]=read();
    for(int i=1;i<n;i++)
    add(read(),read());
    atm.init();
    for(int i=1;i<=n;i++)
    if(d[i]==1)dfs(i,0,0);
    long long ans=0;
    for(int i=1;i<=atm.sz;i++)
    ans+=1ll*(atm.len[i]-atm.len[atm.fa[i]]);
    cout<<ans;
}

 

posted @ 2018-01-18 11:08  orzzz  阅读(194)  评论(0编辑  收藏  举报