您的位置:首页 > 其它

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

2016-12-21 16:51 309 查看

3926: [Zjoi2015]诸神眷顾的幻想乡

Time Limit: 10 Sec  Memory Limit: 512 MB
Submit: 1009  Solved: 596

[Submit][Status][Discuss]

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%的数据,除某两块空地与三个空地相邻外,其他空地都分别至多与两个空地相邻

Source



[Submit][Status][Discuss]

题解:广义后缀自动机。

广义的后缀自动机就是对多串建立的后缀自动机。

对于这个题,他只有20个叶结点,那我们对于每个叶结点建立trie,然后将trie树进行合并,那么所有的路径就包含在trie树中了,是trie树的一条链(不一定从根开始)。

然后对trie树建立后缀自动机,我们在只有一个串的时候p就是直接指向前一个字符的位置,因为我们现在是树结构,所以p指向的应该是该点在trie树中父节点的位置,剩下的建立过程与普通的后缀自动机相同。

如何统计答案呢?在做上一个题的时候说到,对于一个节点以他为结尾的串的贡献就是(len[fa],len[s]]的区间长度,所以统计答案的时候ans+=len[s]-len[fa]即可。

某篇论文中提到,trie 树的后缀自动机的状态数是线性的,不超过2|T|,|T|应该是trie的节点数。

trie树的后缀自动机的转移函数(边数)的上界为trie的节点数*字符集大小。

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
#define N 2000003
#define LL long long
using namespace std;
int n,m,cnt,root,last,np,nq,p,q,tot,ins
;
int ch[N*2][15],v[N*2],fa[N*2],l[N*2],point
,next
,c
;
void add(int x,int y)
{
tot++; next[tot]=point[x]; point[x]=tot; c[tot]=y;
tot++; next[tot]=point[y]; point[y]=tot; c[tot]=x;
}
int extend(int p,int c)
{
np=++cnt; l[np]=l[p]+1;
for (;p&&!ch[p][c];p=fa[p]) ch[p][c]=np;
if (!p) fa[np]=root;
else{
q=ch[p][c];
if (l[q]==l[p]+1) fa[np]=q;
else {
nq=++cnt; l[nq]=l[p]+1;
memcpy(ch[nq],ch[q],sizeof ch[nq]);
fa[nq]=fa[q];
fa[q]=fa[np]=nq;
for (;ch[p][c]==q;p=fa[p]) ch[p][c]=nq;
}
}
return np;
}
void dfs(int x,int fa,int p)
{
int t=extend(p,v[x]);
for (int i=point[x];i;i=next[i])
if (c[i]!=fa) {
dfs(c[i],x,t);
}
}
int main()
{
freopen("a.in","r",stdin);
scanf("%d%d",&n,&m);
last=root=++cnt;
for (int i=1;i<=n;i++) scanf("%d",&v[i]);
for (int i=1;i<n;i++){
int x,y; scanf("%d%d",&x,&y);
add(x,y); ins[x]++; ins[y]++;
}
for (int i=1;i<=n;i++)
if (ins[i]==1) dfs(i,0,1);
LL ans=0;
for (int i=1;i<=cnt;i++)
ans+=(LL)(l[i]-l[fa[i]]);
printf("%I64d\n",ans);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: