BZOJ 3926 诸神眷顾的幻想乡 ( 广义后缀自动机 )

题目链接

题意 : 给出一颗最多有 1e5 个节点的树、其中叶子节点不超过 20 个、每个节点都拥有一种颜色、问你这颗树上有多少种不同的路径使得其路径连起来的颜色串是不一样的。

 

分析 :

这题实际上就是问树上有多少种不同的子串

首先有一个很神奇的性质

所有的子串都会在以某个叶子节点为根的树上以一条直线的姿态出现

学过了后缀自动机的话、对于一个串有多少不同的子串

实际上就是每个节点的 maxLen[i] - maxLen[fa[i]] 累加起来

但是这里有多个串、所以要掏出广义后缀自动机

将每个叶子节点作为根、然后把所有的子串插入到广义后缀自动机上

最后遍历一下广义SAM上所有节点累加答案即可

 

#include<bits/stdc++.h>
#define LL long long
using namespace std;
 
struct General_Suffix_Automaton{
    static const int maxn = ((int)1e6 + 10) * 2;
    static const int Letter = 12;
    int fa[maxn];
    int len[maxn];
    int ch[maxn][Letter];
    int cnt[maxn];
    int tpSum[maxn];
    int tp[maxn];
    int tot;
    int last;
    int mxLen;
 
    inline void init(){
        last = tot = 1;
        len[1] = 0;
        mxLen = 0;
        memset(ch[1], 0, sizeof(ch[1]));
        memset(cnt, 0, sizeof(cnt));
        memset(fa, 0, sizeof(fa));
    }
 
    inline void add(int x){
        int p = last;
        if(ch[p][x] && len[ch[p][x]] == len[p] + 1){
            last = ch[p][x];
            return ;
        }
        int np = ++tot;
        memset(ch[np], 0, sizeof(ch[np]));
        last = np;
        len[np] = len[p] + 1;
        while(p && !ch[p][x]) ch[p][x] = np, p = fa[p];
        if(!p) fa[np] = 1;
        else{
            int q = ch[p][x];
            if(len[q] == len[p] + 1) fa[np] = q;
            else{
                int nq = ++tot;
                len[nq] = len[p] + 1;
                memcpy(ch[nq], ch[q], sizeof(ch[q]));
                fa[nq] = fa[q];
                fa[q] = fa[np] = nq;
                while(p && ch[p][x] == q) ch[p][x] = nq, p = fa[p];
            }
        }
    }
 
    inline void addStr(char * s){
        last = 1;
        int len = strlen(s);
        mxLen = max(mxLen, len);
        for(int i=0; i<len; i++) add(s[i] - 'a'),cnt[last]++;
    }
 
    int deg[maxn];
    inline void toposort(){
        for(int i = 1; i <= mxLen; i++)tpSum[i] = 0;
        for(int i = 1; i <= tot; i++)tpSum[len[i]]++;
        for(int i = 1; i <= mxLen; i++)tpSum[i] += tpSum[i-1];
        for(int i = 1; i <= tot; i++)tp[tpSum[len[i]]--] = i;
        for(int i = tot; i; i--)cnt[fa[tp[i]]] += cnt[tp[i]];
 
//        queue<int> que;
//        while(!que.empty()) que.pop();
//        int tmp = tot;
//        for(int i=1; i<=tot; i++) deg[fa[i]]++;
//        for(int i=1; i<=tot; i++) if(!deg[i]) que.push(i);
//        while(!que.empty()){
//            int x = que.front();
//            que.pop();
//            tp[tmp--] = x;
//            deg[fa[x]]--;
//            if(!deg[fa[x]]) que.push(fa[x]);
//        }
//        for(int i=tot; i>=1; i--) cnt[fa[tp[i]]] += cnt[tp[i]];
    }
 
    inline void GetAns(){
        LL ans = 0;
        for(int i=1; i<=tot; i++){
            ans += 1LL * len[i] - 1LL * len[fa[i]];
        }
        printf("%lld\n", ans);
    }
 
}sam;
 
const int maxn = 1e5 + 10;
 
struct EDGE{ int v, nxt; }Edge[maxn<<2];
int Head[maxn], EdgeCnt;
 
inline void EdgeInit()
{
    memset(Head, -1, sizeof(Head));
    EdgeCnt = 0;
}
 
inline void AddEdge(int from, int to)
{
    int &cnt = EdgeCnt;
    Edge[cnt].v = to;
    Edge[cnt].nxt = Head[from];
    Head[from] = cnt++;
}
 
int Col[maxn];
int D[maxn];
 
inline void DFS(int v, int fa)
{
    sam.add(Col[v]);
    int cur = sam.last;
    for(int i=Head[v]; i!=-1; i=Edge[i].nxt){
        int Eiv = Edge[i].v;
        if(Eiv == fa) continue;
        DFS(Eiv, v);
        sam.last = cur;
    }
}
 
int main(void)
{
    int n, c;
    scanf("%d %d", &n, &c);
 
    for(int i=1; i<=n; i++) scanf("%d", &Col[i]);
 
    EdgeInit();
 
    for(int i=1; i<=n-1; i++){
        int u, v;
        scanf("%d %d", &u, &v);
        D[u]++, D[v]++;
        AddEdge(u, v);
        AddEdge(v, u);
    }
 
    sam.init();
 
    for(int i=1; i<=n; i++){
        if(D[i] == 1){
            sam.last = 1;
            DFS(i, 0);
        }
    }
 
    sam.GetAns();
 
    return 0;
}
View Code

 

posted @ 2018-11-10 20:05  qwerity  阅读(156)  评论(0编辑  收藏  举报