码迷,mamicode.com
首页 > 其他好文 > 详细

bzoj3926 [Zjoi2015]诸神眷顾的幻想乡

时间:2017-10-06 13:54:22      阅读:97      评论:0      收藏:0      [点我收藏+]

标签:put   style   input   fine   多少   trie   端点   cout   选择   

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

HINT

对于所有数据,1<=n<=100000, 1<=c<=10。 

对于15%的数据,n<=2000。 
另有5%的数据,所有空地都至多与两个空地相邻。 
另有5%的数据,除一块空地与三个空地相邻外,其他空地都分别至多与两个空地相邻。 
另有5%的数据,除某两块空地与三个空地相邻外,其他空地都分别至多与两个空地相邻

 

正解:广义后缀自动机。

学习了一波广义后缀自动机。。广义后缀自动机,也就是用$trie$树构造后缀自动机。

好像和普通的后缀自动机还是差不多的,只是构广义后缀自动机还需要在当前已经存在这个状态的情况下加一些特判,具体看代码吧。。

然后这道题的答案就是每个状态的$len$减去它父亲的$len$。

 

 1 #include <bits/stdc++.h>
 2 #define il inline
 3 #define RG register
 4 #define ll long long
 5 #define M (4000010)
 6 #define N (100010)
 7 
 8 using namespace std;
 9 
10 struct edge{ int nt,to; }g[N<<1];
11 
12 int ch[M][10],fa[M],l[M],q[M],vis[M],la,tot;
13 int d[N],s[N],nxt[N],head[N],S,n,num;
14 ll ans;
15 
16 il int gi(){
17   RG int x=0,q=1; RG char ch=getchar();
18   while ((ch<0 || ch>9) && ch!=-) ch=getchar();
19   if (ch==-) q=-1,ch=getchar();
20   while (ch>=0 && ch<=9) x=x*10+ch-48,ch=getchar();
21   return q*x;
22 }
23 
24 il void insert(RG int from,RG int to){
25   g[++num]=(edge){head[from],to},head[from]=num; return;
26 }
27 
28 il int add(RG int p,RG int c){
29   RG int np=ch[p][c];
30   if (np && l[np]==l[p]+1) return np;
31   l[np=++tot]=l[p]+1;
32   for (;p && !ch[p][c];p=fa[p]) ch[p][c]=np;
33   if (!p){ fa[np]=1; return np; } RG int q=ch[p][c];
34   if (l[q]==l[p]+1) fa[np]=q; else{
35     RG int nq=++tot; l[nq]=l[p]+1;
36     fa[nq]=fa[q],fa[q]=fa[np]=nq;
37     memcpy(ch[nq],ch[q],sizeof(ch[q]));
38     for (;ch[p][c]==q;p=fa[p]) ch[p][c]=nq;
39   }
40   return np;
41 }
42 
43 il void dfs(RG int x,RG int p){
44   nxt[x]=add(nxt[p],s[x]);
45   for (RG int i=head[x];i;i=g[i].nt)
46     if (g[i].to!=p) dfs(g[i].to,x);
47   return;
48 }
49 
50 il void bfs(){
51   RG int h=0,t=1; q[t]=1,vis[1]=1;
52   while (h<t){
53     RG int x=q[++h],v;
54     for (RG int i=0;i<S;++i){
55       v=ch[x][i]; if (!v || vis[v]) continue;
56       ans+=l[v]-l[fa[v]],vis[v]=1,q[++t]=v;
57     }
58   }
59   return;
60 }
61 
62 int main(){
63 #ifndef ONLINE_JUDGE
64   freopen("substring.in","r",stdin);
65   freopen("substring.out","w",stdout);
66 #endif
67   n=gi(),S=gi(),nxt[0]=la=++tot;
68   for (RG int i=1;i<=n;++i) s[i]=gi();
69   for (RG int i=1,u,v;i<n;++i)
70     u=gi(),v=gi(),++d[u],++d[v],insert(u,v),insert(v,u);
71   for (RG int i=1;i<=n;++i) if (d[i]==1) dfs(i,0);
72   bfs(),cout<<ans; return 0;
73 }

 

bzoj3926 [Zjoi2015]诸神眷顾的幻想乡

标签:put   style   input   fine   多少   trie   端点   cout   选择   

原文地址:http://www.cnblogs.com/wfj2048/p/7631444.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!