bzoj 3926 [Zjoi20150]诸神眷顾的幻想乡(SAM)

 

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

Time Limit: 10 Sec  Memory Limit: 512 MB
Submit: 615  Solved: 369
[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%的数据,除某两块空地与三个空地相邻外,其他空地都分别至多与两个空地相邻
 
 
【思路】
 
  从每个叶子开始dfs,构建广义后缀自动机,答案为l[i]-l[fa[i]]的和。
  从每个叶子dfs相当于将每个叶子为根做棵树然后构建SAM,这样使节点间的路径转化为从起点开始到子孙的路径。在SAM上,从起点到每一个点的不同路径是以该这个点为最后一个字符的字符串的不同后缀,长度介于(l[fa[i]],l[i]]之间的,个数就是l[i]-l[fa[i]]。
  关于广义后缀自动机:

传统后缀自动机是解决单个主串的匹配问题,广义后缀自动机可以用来解决多个主串的匹配问题。

      如何将多个主串构建成广义后缀自动机?先将一个主串建立成后缀自动机,让后将重置last,令last=root,下一个字符串再从头节点开始建立,下一状态如果不存在,则以后缀自动机的规则进行建立新节点。

      如果下一状态已经建立,我们直接转移到该状态即可,既然到达该状态,说明已经匹配成功的字符串的所有后缀都在该状态及该状态的父节点,父节点的父节点...直到root,所以我们需要对该状态以及他的父节点,他的父节点的父节点。。。直到root进行内容更新,更新的内容当然因题目而异,如果求某个字符串出现的个数,就cnt++,如果求某个字符串出现的位置,就将位置存在结点的一个数组里。

 

                                    Quote from here

 
 
【代码】
 
#include<cstdio>
#include<vector>
#include<cstring>
#include<iostream>
using namespace std;

const int N = 4*1e6+10;

vector<int> g[N];
int n,m,c[N],in[N];

int sz,fa[N],ch[N][10],l[N];
int add(int c,int p) {
	int np=++sz; l[np]=l[p]+1;
	for(;p&&!ch[p][c];p=fa[p]) ch[p][c]=np;
	if(!p) fa[np]=1;
	else {
		int q=ch[p][c];
		if(l[q]==l[p]+1) fa[np]=q;
		else {
			int nq=++sz; l[nq]=l[p]+1;
			memcpy(ch[nq],ch[q],sizeof(ch[q]));
			fa[nq]=fa[q];
			fa[np]=fa[q]=nq;
			for(;q==ch[p][c];p=fa[p]) ch[p][c]=nq;
		}
	}
	return np;
}
void dfs(int u,int fa,int p) {
	int t=add(c[u],p);
	for(int i=0;i<g[u].size();i++) {
		int v=g[u][i];
		if(v!=fa) dfs(v,u,t);
	}
}

void read(int& x) {
	char c=getchar(); int f=1; x=0;
	while(!isdigit(c)) {if(c=='-')f=-1; c=getchar();}
	while(isdigit(c)) x=x*10+c-'0',c=getchar();
	x*=f;
}
int main() {
	//freopen("in.in","r",stdin);
	//freopen("out.out","w",stdout);
	read(n),read(m);
	for(int i=1;i<=n;i++) read(c[i]);
	int u,v;
	for(int i=0;i<n-1;i++) {
		read(u),read(v);
		g[u].push_back(v);
		g[v].push_back(u);
		in[v]++,in[u]++;
	}
	sz=1;
	for(int i=1;i<=n;i++)
		if(in[i]==1) dfs(i,-1,1);
	long long ans=0;
	for(int i=1;i<=sz;i++)
		ans+=l[i]-l[fa[i]];
	printf("%lld",ans);	
	return 0;
}

  

 

你可能感兴趣的:(bzoj 3926 [Zjoi20150]诸神眷顾的幻想乡(SAM))