BZOJ3926: [Zjoi2015]诸神眷顾的幻想乡(广义后缀自动机)

Description

 幽香是全幻想乡里最受人欢迎的萌妹子,这天,是幽香的2600岁生日,无数幽香的粉丝到了幽香家门前的太阳花田上来为幽香庆祝生日。 

粉丝们非常热情,自发组织表演了一系列节目给幽香看。幽香当然也非常高兴啦。 
这时幽香发现了一件非常有趣的事情,太阳花田有n块空地。在过去,幽香为了方便,在这n块空地之间修建了n-1条边将它们连通起来。也就是说,这n块空地形成了一个树的结构。 
有n个粉丝们来到了太阳花田上。为了表达对幽香生日的祝贺,他们选择了c中颜色的衣服,每种颜色恰好可以用一个0到c-1之间的整数来表示。并且每个人都站在一个空地上,每个空地上也只有一个人。这样整个太阳花田就花花绿绿了。幽香看到了,感觉也非常开心。 
粉丝们策划的一个节目是这样的,选中两个粉丝A和B(A和B可以相同),然后A所在的空地到B所在的空地的路径上的粉丝依次跳起来(包括端点),幽香就能看到一个长度为A到B之间路径上的所有粉丝的数目(包括A和B)的颜色序列。一开始大家打算让人一两个粉丝(注意:A,B和B,A是不同的,他们形成的序列刚好相反,比如红绿蓝和蓝绿红)都来一次,但是有人指出这样可能会出现一些一模一样的颜色序列,会导致审美疲劳。 
于是他们想要问题,在这个树上,一共有多少可能的不同的颜色序列(子串)幽香可以看到呢? 
太阳花田的结构比较特殊,只与一个空地相邻的空地数量不超过20个。 

Input

 第一行两个正整数n,c。表示空地数量和颜色数量。 

第二行有n个0到c-1之间,由空格隔开的整数,依次表示第i块空地上的粉丝的衣服颜色。(这里我们按照节点标号从小到大的顺序依次给出每块空地上粉丝的衣服颜色)。 
接下来n-1行,每行两个正整数u,v,表示有一条连接空地u和空地v的边。 

Output

 一行,输出一个整数,表示答案。 

Sample Input

7 3
0 2 1 2 1 0 0
1 2
3 4
3 5
4 6
5 7
2 5

Sample Output

30

解题思路:

可能是题目描述最吓人的题了。

这道题最重要的问题就是如何将所有字符串遍历一遍。

考虑正常走会有什么问题,那就是可能有字符串被起点分开了。

那么只需要在末端开始遍历就可以处理了。

树的末端就是叶节点。

20个还不多,遍历20次。

最后在后缀自动机上统计答案。

代码:

 1 #include<cstdio>
 2 #include<algorithm>
 3 typedef long long lnt;
 4 struct pnt{
 5     int hd;
 6     int val;
 7     int ind;
 8 }p[3000000];
 9 struct sant{
10     int tranc[10];
11     int pre;
12     int len;
13 }s[3000000];
14 struct ent{
15     int twd;
16     int lst;
17 }e[3000000];
18 int siz;
19 int cnt;
20 int n,c;
21 void ade(int f,int t)
22 {
23     cnt++;
24     e[cnt].twd=t;
25     e[cnt].lst=p[f].hd;
26     p[f].hd=cnt;
27     p[t].ind++;
28     return ;
29 }
30 int Insert(int c,int fin)
31 {
32     int nwp,lsp,nwq,lsq;
33     nwp=++siz;
34     s[nwp].len=s[fin].len+1;
35     for(lsp=fin;lsp&&!s[lsp].tranc[c];lsp=s[lsp].pre)
36         s[lsp].tranc[c]=nwp;
37     if(!lsp)
38         s[nwp].pre=1;
39     else{
40         lsq=s[lsp].tranc[c];
41         if(s[lsq].len==s[lsp].len+1)
42             s[nwp].pre=lsq;
43         else{
44             nwq=++siz;
45             s[nwq]=s[lsq];
46             s[nwq].len=s[lsp].len+1;
47             s[lsq].pre=s[nwp].pre=nwq;
48             while(s[lsp].tranc[c]==lsq)
49             {
50                 s[lsp].tranc[c]=nwq;
51                 lsp=s[lsp].pre;
52             }
53         }
54     }
55     return nwp;
56 }
57 void Sam_dfs(int x,int f,int fin)
58 {
59     fin=Insert(p[x].val,fin);
60     for(int i=p[x].hd;i;i=e[i].lst)
61     {
62         int to=e[i].twd;
63         if(to==f)
64             continue;
65         Sam_dfs(to,x,fin);
66     }
67     return ;
68 }
69 int main()
70 {
71     //freopen("a.in","r",stdin);
72     siz++;
73     scanf("%d%d",&n,&c);
74     for(int i=1;i<=n;i++)
75         scanf("%d",&p[i].val);
76     for(int i=1;i<n;i++)
77     {
78         int a,b;
79         scanf("%d%d",&a,&b);
80         ade(a,b);
81         ade(b,a);
82     }
83     for(int i=1;i<=n;i++)
84         if(p[i].ind==1)
85             Sam_dfs(i,i,1);
86     lnt ans=0;
87     for(int i=1;i<=siz;i++)
88         ans+=(lnt)(s[i].len-s[s[i].pre].len);
89     printf("%lld
",ans);
90     return 0;
91 }
原文地址:https://www.cnblogs.com/blog-Dr-J/p/10084685.html