1. 程式人生 > 實用技巧 >[ZJOI2015] 諸神眷顧的幻想鄉 - 廣義字尾自動機,樹

[ZJOI2015] 諸神眷顧的幻想鄉 - 廣義字尾自動機,樹

Description

給定一棵葉子結點數量 \(\le 20\) 的樹,在樹上任選一條路,問有多少種顏色序列不同的選擇。

Solution

一條路徑一定屬於以某個葉子為根的樹上所有的直鏈的集合

於是我們把以每個葉子為根的樹 DFS 一遍,當作一個 Trie 插入廣義字尾自動機即可

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 2000005;

int n,c,a[N],t1,t2,t3,vis[N],pos[N],d[N];
vector <int> g[N];

struct SAM {
    int len[N], ch[N][10], fa[N], ind, last;
    SAM() { ind = last = 1; }
    inline int extend(int id) {
        if(ch[last][id] && len[last]+1==len[ch[last][id]]) return ch[last][id]; //!
        int cur = (++ ind), p, tmp, flag = 0; //!
        len[cur] = len[last] + 1;
        for (p = last; p && !ch[p][id]; p = fa[p]) ch[p][id] = cur;
        if (!p) fa[cur] = 1;
        else {
            int q = ch[p][id];
            if (len[q] == len[p] + 1) fa[cur] = q;
            else {
                if(p==last) flag=1; //!
                tmp = (++ ind);
                len[tmp] = len[p] + 1;
                for(int i=0;i<10;i++) ch[tmp][i] = ch[q][i];
                fa[tmp] = fa[q];
                for (; p && ch[p][id] == q; p = fa[p]) ch[p][id] = tmp;
                fa[cur] = fa[q] = tmp;
            }
        }
        last = cur;
        return flag ? tmp : cur;//!
    }
    void dfs(int p)
    {
        vis[p]=1;
        for(int q:g[p])
        {
            if(!vis[q])
            {
                last=pos[p];
                pos[q]=extend(a[q]);
                dfs(q);
            }
        }
    }
    void ins(int rt)
    {
        memset(vis,0,sizeof vis);
        memset(pos,0,sizeof pos);
        last=1;
        pos[rt]=extend(a[rt]);
        dfs(rt);
    }
    int getans()
    {
        int ans=0;
        for(int i=1;i<=ind;i++) ans+=len[i]-len[fa[i]];
        return ans;
    }
} sam;

signed main() {
    ios::sync_with_stdio(false);
    cin>>n>>c;
    for(int i=1;i<=n;i++) cin>>a[i];
    for(int i=1;i<n;i++)
    {
        cin>>t1>>t2;
        g[t1].push_back(t2);
        g[t2].push_back(t1);
        d[t1]++;
        d[t2]++;
    }
    for(int i=1;i<=n;i++)
    {
        if(d[i]==1)
        {
            sam.ins(i);
        }
    }
    cout<<sam.getans()<<endl;
}