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—诸神眷顾的幻想乡的更多相关文章

  1. bzoj3926: [Zjoi2015]诸神眷顾的幻想乡 对[广义后缀自动机]的一些理解

    先说一下对后缀自动机的理解,主要是对构造过程的理解. 构造中,我们已经得到了前L个字符的后缀自动机,现在我们要得到L+1个字符的后缀自动机,什么需要改变呢? 首先,子串$[0,L+1)$对应的状态不存 ...

  2. BZOJ3926 [Zjoi2015]诸神眷顾的幻想乡 字符串 SAM

    原文链接https://www.cnblogs.com/zhouzhendong/p/BZOJ3926.html 题目传送门 - BZOJ3926 题意 给定一个有 $n$ 个节点,最多只有 $20$ ...

  3. BZOJ3926:[ZJOI2015]诸神眷顾的幻想乡(广义SAM)

    Description 幽香是全幻想乡里最受人欢迎的萌妹子,这天,是幽香的2600岁生日,无数幽香的粉丝到了幽香家门前的太阳花田上来为幽香庆祝生日. 粉丝们非常热情,自发组织表演了一系列节目给幽香看. ...

  4. [BZOJ3926][ZJOI2015]诸神眷顾的幻想乡(后缀自动机)

    日,无数幽香的粉丝到了幽香家门前的太阳花田上来为幽香庆祝生日. 粉丝们非常热情,自发组织表演了一系列节目给幽香看.幽香当然也非常高兴啦.  这时幽香发现了一件非常有趣的事情,太阳花田有n块空地.在过去 ...

  5. BZOJ3926 Zjoi2015 诸神眷顾的幻想乡【广义后缀自动机】

    Description 幽香是全幻想乡里最受人欢迎的萌妹子,这天,是幽香的2600岁生日,无数幽香的粉丝到了幽香家门前的太阳花田上来为幽香庆祝生日. 粉丝们非常热情,自发组织表演了一系列节目给幽香看. ...

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

    题目 幽香是全幻想乡里最受人欢迎的萌妹子,这天,是幽香的2600岁生日,无数幽香的粉丝到了幽香家门前的太阳花田上来为幽香庆祝生日. 粉丝们非常热情,自发组织表演了一系列节目给幽香看.幽香当然也非常高兴 ...

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

    Description 幽香是全幻想乡里最受人欢迎的萌妹子,这天,是幽香的2600岁生日,无数幽香的粉丝到了幽香家门前的太阳花田上来为幽香庆祝生日. 粉丝们非常热情,自发组织表演了一系列节目给幽香看. ...

  8. BZOJ3926 ZJOI2015诸神眷顾的幻想乡(广义后缀自动机)

    对多串建立SAM的一种方法是建trie再对trie建SAM.构造方式分为在线(也即不建trie而是依次插入每个串,或在trie上dfs)和离线(也即建好trie再bfs).其中离线构造与单串的构造方式 ...

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

    #include <iostream> #include <cstdio> #include <cstring> #include <cmath> #i ...

  10. BZOJ3926 ZJOI2015 诸神眷顾的幻想乡 Trie、广义SAM

    传送门 树上的任意一条路径一定会在以某一个叶子节点为根的树上成为一条直上直下的链,而总共只有\(20\)个叶子节点. 于是每一次选所有叶子节点中的一个作为根,形成一个\(Trie\),把\(20\)个 ...

随机推荐

  1. Analysis 图标分析 - loadrunner

    analysis常见 /

  2. SICP读书笔记 2.3

    SICP CONCLUSION 让我们举起杯,祝福那些将他们的思想镶嵌在重重括号之间的Lisp程序员 ! 祝我能够突破层层代码,找到住在里计算机的神灵! 目录 1. 构造过程抽象 2. 构造数据抽象 ...

  3. IO多路复用多并发服务器模板

    import socket import selectors # IO多路复用选择器 epoll_selector = selectors.EpollSelector() # 实例化选择器 serve ...

  4. vue 动画

    Vue 在插入.更新或者移除 DOM 时,提供多种不同方式的应用过渡效果.包括以下几种常见的方式: 在 CSS 过渡和动画中自动应用 class 可以配合使用第三方 CSS 动画库,如 Animate ...

  5. Netty源码分析第7章(编码器和写数据)---->第4节: 刷新buffer队列

    Netty源码分析第七章: 编码器和写数据 第四节: 刷新buffer队列 上一小节学习了writeAndFlush的write方法, 这一小节我们剖析flush方法 通过前面的学习我们知道, flu ...

  6. GIT rebase讲解

    对分支进行rebase 从master分支checkout出fork分支,并在master和fork上都进行了一些修改 现在fork分支想要及时的同步master分支上的修改,避免在已经失效的代码上继 ...

  7. 图解 Go 并发

    你很可能从某种途径听说过 Go 语言.它越来越受欢迎,并且有充分的理由可以证明. Go 快速.简单,有强大的社区支持.学习这门语言最令人兴奋的一点是它的并发模型. Go 的并发原语使创建多线程并发程序 ...

  8. python3.6环境中django2.0与xadmin0.6结合的后台管理

    1.xadmin简介 django的admin管理后台页面很简洁,对个人来说做后台管理非常简单:xadmin的比较admin优化界面,看着也舒服. xadmin界面效果如下: 2.xadmin安装 从 ...

  9. 3.控制hive map reduce个数

    参考: https://blog.csdn.net/wuliusir/article/details/45010129 https://blog.csdn.net/zhong_han_jun/arti ...

  10. React Native 之 main.jsbundle生成方法

    通过react-native init yooweiProject 生成的RN项目(版本基于0.57),目录结构如下 项目结构: 大家可以发现main.jsbundle 是红色的,不存在的,这个属于正 ...