1. 程式人生 > >BZOJ 3926 諸神眷顧的幻想鄉 ( 廣義字尾自動機 )

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