【BZOJ3926】诸神眷顾的幻想乡 【广义后缀自动机】
题意
给定一棵树,每个结点有一个颜色,问树上有多少种子串(定义子串为两点上路径颜色的序列)。保证叶子结点<=20
分析
我们可以发现一个结论,任意一个子串一定是以某个叶子结点为根的trie的后缀。我们有注意到,叶子节点最多只有20,那么我们可以将每个叶子结点拿出来,以它为根按照trie树的方式插到广义后缀自动机中。要统计一共有多少子串的话,这样这样建好自动机以后枚举每个状态,然后res+=st[u].len-st[st[u].link].len;
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 #include <iostream> 5 6 using namespace std; 7 const int maxn=100000+100; 8 typedef long long LL; 9 int val[maxn]; 10 int head[maxn],Next[2*maxn],to[2*maxn],deg[maxn]; 11 int N,sz,C; 12 int cnt,cur; 13 struct state{ 14 int len,link; 15 int next[12]; 16 }st[40*maxn]; 17 18 void init(){ 19 sz=0; 20 memset(head,-1,sizeof(head)); 21 cnt=1; 22 cur=0; 23 st[0].link=-1; 24 st[0].len=0; 25 } 26 27 void add_edge(int a,int b){ 28 ++sz; 29 to[sz]=b; 30 Next[sz]=head[a]; 31 head[a]=sz; 32 } 33 34 int build_sam(int c,int last){ 35 cur=cnt++; 36 st[cur].len=st[last].len+1; 37 int p; 38 for(p=last;p!=-1&&st[p].next[c]==0;p=st[p].link) 39 st[p].next[c]=cur; 40 if(p==-1) 41 st[cur].link=0; 42 else{ 43 int q=st[p].next[c]; 44 if(st[q].len==st[p].len+1) 45 st[cur].link=q; 46 else{ 47 int clone=cnt++; 48 st[clone].len=st[p].len+1; 49 st[clone].link=st[q].link; 50 for(int i=0;i<C;i++) 51 st[clone].next[i]=st[q].next[i]; 52 for(;p!=-1&&st[p].next[c]==q;p=st[p].link) 53 st[p].next[c]=clone; 54 st[cur].link=st[q].link=clone; 55 } 56 } 57 return cur; 58 } 59 60 void dfs(int u,int fa,int las){ 61 int Las=build_sam(val[u],las); 62 for(int i=head[u];i!=-1;i=Next[i]){ 63 int v=to[i]; 64 if(v==fa)continue; 65 dfs(v,u,Las); 66 } 67 return; 68 } 69 70 int main(){ 71 scanf("%d%d",&N,&C); 72 for(int i=1;i<=N;i++) 73 scanf("%d",&val[i]); 74 int a,b; 75 init(); 76 for(int i=1;i<N;i++){ 77 scanf("%d%d",&a,&b); 78 add_edge(a,b); 79 add_edge(b,a); 80 deg[a]++;deg[b]++; 81 } 82 for(int i=1;i<=N;i++){ 83 if(deg[i]==1){ 84 dfs(i,-1,0); 85 } 86 } 87 LL ans=0; 88 for(int i=0;i<cnt;i++){ 89 int p=st[i].link; 90 if(p!=-1){ 91 ans+=st[i].len-st[p].len; 92 } 93 } 94 printf("%lld\n",ans); 95 return 0; 96 }