BZOJ 3926 ZJOI2015 诸神眷顾的幻想乡 广义后缀自动机
题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=3926
题意概述:给出一棵N个结点的树,树上最多有20个叶子,每个点有颜色,编号0~9。求树上不同的颜色路径(由一个点到另一个点的路径上的点的颜色按顺序排列而成)数量。N<=100000.
当你发现只有20个叶子的时候你就很开心了因为你发现可以乱搞了。
使用秘技——广义后缀自动机来节省空间(实际上广义后缀自动机是对一棵trie做的自动机,解决多母串问题)。可以直接把原树分别以20个结点为根看成20棵trie,弄到SAM里面去。在树的分支处就让这个结点来充当所有儿子的last,仔细一想你发现SAM的性质并没有改变,并且trie的不同分支之间没有影响(说白了广义后缀自动机就是一个能够识别trie中某个结点开始的所有后缀的自动机)。
统计答案的时候直接用每个状态的MAX减去其parent的MAX就可以得到答案。
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<cstdlib> 5 #include<algorithm> 6 #include<cmath> 7 #include<queue> 8 #include<set> 9 #include<map> 10 #include<vector> 11 #include<cctype> 12 using namespace std; 13 const int MAXN=100005; 14 typedef long long LL; 15 16 int N,C,c[MAXN]; 17 struct edge{ int to,next; }E[MAXN<<1]; 18 int first[MAXN],np; 19 struct SAM{ 20 static const int maxn=4000005; 21 static const int sigma_sz=10; 22 int sz,to[maxn][sigma_sz],mx[maxn],pa[maxn]; 23 SAM(){ sz=1; } 24 int newnode(){ 25 memset(to[++sz],0,sizeof(to[sz])); 26 mx[sz]=pa[sz]=0; 27 return sz; 28 } 29 int extend(int w,int p){ 30 int np=newnode(); 31 mx[np]=mx[p]+1; 32 while(p&&!to[p][w]) to[p][w]=np,p=pa[p]; 33 if(!p) pa[np]=1; 34 else{ 35 int q=to[p][w]; 36 if(mx[q]==mx[p]+1) pa[np]=q; 37 else{ 38 int nq=newnode(); mx[nq]=mx[p]+1; 39 memcpy(to[nq],to[q],sizeof(to[nq])); 40 pa[nq]=pa[q]; 41 pa[q]=pa[np]=nq; 42 while(p&&to[p][w]==q) to[p][w]=nq,p=pa[p]; 43 } 44 } 45 return np; 46 } 47 LL calc(){ 48 LL re=0; 49 for(int i=2;i<=sz;i++) re+=mx[i]-mx[pa[i]]; 50 return re; 51 } 52 }sam; 53 54 void _scanf(int &x) 55 { 56 x=0; 57 char ch=getchar(); 58 while(ch<'0'||ch>'9') ch=getchar(); 59 while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar(); 60 } 61 void add_edge(int u,int v) 62 { 63 E[++np]=(edge){v,first[u]}; 64 first[u]=np; 65 } 66 void data_in() 67 { 68 _scanf(N);_scanf(C); 69 for(int i=1;i<=N;i++) _scanf(c[i]); 70 int x,y; 71 for(int i=1;i<N;i++){ 72 _scanf(x);_scanf(y); 73 add_edge(x,y); add_edge(y,x); 74 } 75 } 76 void DFS(int i,int f,int p) 77 { 78 int pp=sam.extend(c[i],p); 79 for(int p=first[i];p;p=E[p].next){ 80 int j=E[p].to; 81 if(j==f) continue; 82 DFS(j,i,pp); 83 } 84 } 85 void work() 86 { 87 for(int i=1;i<=N;i++) 88 if(!E[first[i]].next) DFS(i,0,1); 89 cout<<sam.calc()<<'\n'; 90 } 91 int main() 92 { 93 data_in(); 94 work(); 95 return 0; 96 }