首页 > 代码库 > 【bzoj3926】 Zjoi2015—诸神眷顾的幻想乡
【bzoj3926】 Zjoi2015—诸神眷顾的幻想乡
http://www.lydsy.com/JudgeOnline/problem.php?id=3926 (题目链接)
题意
给出一棵树,每个节点有一个编号,范围在${[0,9]}$。一个序列是指树上某条路径上的点的编号按顺序依次相接连成的字符串。问有多少个不同的序列。
Solution
广义后缀自动机,看起来好高深的样子,然而题解却很简单。这道题好像只是一个很简单的应用吧。
首先题目数据范围有一个很特殊的地方:叶子节点数${<=20}$。嘿嘿嘿,这就可以搞事情了。
我们往叶子节点上想,慢慢就会发现一个结论:任意一个存在的序列都可以以某个叶子节点为根进行dfs得到。
所以,我们对于每个叶子节点建立一个${trie}$树,将这些${trie}$树构成一个广义后缀自动机,最后在后缀自动机中扫一遍每一个状态,它所贡献的不同序列个数就是它的${len}$减父亲的${len}$。
下面引用一段广义后缀自动机的介绍,来自:http://blog.csdn.net/wangzhen_yu/article/details/45481269
广义后缀自动机:
传统后缀自动机是解决单个主串的匹配问题,广义后缀自动机可以用来解决多个主串的匹配问题,这样我们就不用把所有主串接在一起构造自动机了,大大节省了时间空间。
如何将多个主串构建成广义后缀自动机?先将一个主串建立成后缀自动机,让后将重置last,令last=root,下一个字符串再从头节点开始建立,下一状态如果不存在,则以后缀自动机的规则进行建立新节点。
如果下一状态已经建立,我们直接转移到该状态即可,既然到达该状态,说明已经匹配成功的字符串的所有后缀都在该状态及该状态的父节点,父节点的父节点...直到root,所以我们需要对该状态以及他的父节点,他的父节点的父节点。。。直到root进行内容更新,更新的内容当然因题目而异,如果求某个字符串出现的个数,就cnt++,如果求某个字符串出现的位置,就将位置存在结点的一个数组里。
细节
因为是广义后缀自动机,注意数组大小。答案开LL。
代码
// bzoj3926#include<algorithm>#include<iostream>#include<cstdlib>#include<cstring>#include<cstdio>#include<cmath>#include<ctime>#define RG register#define LL long long#define inf (1ll<<30)#define MOD 1000000007#define Pi acos(-1.0)#define free(a) freopen(a".in","r",stdin),freopen(a".out","w",stdout);using namespace std;const int maxn=2000010;int ch[maxn<<1][10],par[maxn<<1],len[maxn<<1],Dargen,sz;int head[maxn],deg[maxn],a[maxn],n,C,cnt;struct edge {int to,next;}e[maxn<<1];void link(int u,int v) { e[++cnt]=(edge){v,head[u]};head[u]=cnt; e[++cnt]=(edge){u,head[v]};head[v]=cnt;}int Extend(int c,int p) { int np=++sz; len[np]=len[p]+1; for (;p && !ch[p][c];p=par[p]) ch[p][c]=np; if (!p) par[np]=Dargen; else { int q=ch[p][c]; if (len[q]==len[p]+1) par[np]=q; else { int nq=++sz;len[nq]=len[p]+1; memcpy(ch[nq],ch[q],sizeof(ch[q])); par[nq]=par[q]; par[np]=par[q]=nq; for (;p && ch[p][c]==q;p=par[p]) ch[p][c]=nq; } } return np;}void dfs(int x,int fa,int p) { int t=Extend(a[x],p); for (int i=head[x];i;i=e[i].next) if (e[i].to!=fa) dfs(e[i].to,x,t);}int main() { scanf("%d%d",&n,&C); for (int i=1;i<=n;i++) scanf("%d",&a[i]); for (int u,v,i=1;i<n;i++) { scanf("%d%d",&u,&v); link(u,v);deg[u]++;deg[v]++; } Dargen=sz=1; for (int i=1;i<=n;i++) if (deg[i]==1) dfs(i,0,1); LL ans=0; for (int i=1;i<=sz;i++) ans+=len[i]-len[par[i]]; printf("%lld",ans); return 0;}
【bzoj3926】 Zjoi2015—诸神眷顾的幻想乡