【BZOJ 3926】[Zjoi2015]诸神眷顾的幻想乡 广义后缀自动机

注意题目描述:叶子节点<=20,然后如果不考虑序列恰好相反的情况,那么我们从一个根节点出发遍历这棵树,这棵树上能构成的字符串一定是从此根节点到其中一个叶子节点的路径所构成的字符串的字串,那么及时要考虑序列顺序恰好相反的情况,叶子节点又那么少,直接暴力从每一个叶子节点找一下就好了。

至于广义后缀自动机的话,额,在脑海里YY一下,其实我们的后缀自动机最终构造出来是一条链,然后旁边多了一些奇奇怪怪虚拟节点的对吧,那么我们每一条链其实可以代表不同的串,然后再把这些不同的串之间能够公用的部分合理公用(丧心病狂),得到一个类似于树形结构的东东,这个就是广义后缀自动机啦,唯一的区别就在于p和np的关系上,自己慢慢看吧。

#include<cstdio>
#include<cstring>
#include<iostream>
#define maxn 400020
#define LL long long
using namespace std;
int son[maxn*10][12],head[maxn*10],len[maxn*10],fail[maxn*10],n,col[maxn],cnt=1;
int tot=1,rt=1,times[maxn];
LL ans;
struct edge{int next,v;}e[maxn];
void adde(int a,int b){e[cnt].v=b,e[cnt].next=head[a];head[a]=cnt++;}

int insert(int p,int x){
	int np=++tot,q,nq;
	len[np]=len[p]+1;
	while(p&&!son[p][x])son[p][x]=np,p=fail[p];
	if(p==0)fail[np]=rt;
	else{
		int q=son[p][x];
		if(len[q]==len[p]+1)fail[np]=q;
		else{
			nq=++tot;len[nq]=len[p]+1;
			memcpy(son[nq],son[q],sizeof(son[q]));
			fail[nq]=fail[q];
			fail[q]=fail[np]=nq;
			while(p&&son[p][x]==q)son[p][x]=nq,p=fail[p];
		}
	}
	return np;
}

void dfs(int u,int fa,int last){
	int p=insert(last,col[u]);
	for(int v,i=head[u];i;i=e[i].next){
		if((v=e[i].v)==fa)continue;
		dfs(v,u,p);
	}
}

int main(){
	int c;
	scanf("%d%d",&n,&c);
	for(int i=1;i<=n;i++)scanf("%d",col+i);
	for(int a,b,i=1;i<n;i++){
		scanf("%d%d",&a,&b);
		adde(a,b),adde(b,a);
		times[a]++,times[b]++;
	}
	for(int i=1;i<=n;i++)if(times[i]==1){
		dfs(i,0,1);
	}
	for(int i=2;i<=tot;i++)
		ans+=(LL)len[i]-len[fail[i]];
	printf("%lld",ans);
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值