Luogu P3346 [ZJOI2015]诸神眷顾的幻想乡 广义SAM 后缀自动机
题目链接 \(Click\) \(Here\)
真的是好题啊~不过在说做法之前先强调几个自己总是掉的坑点。
更新节点永远记不住往上跳\(p = fa[p]\)
新建节点永远记不住\(len[y] = len[p] + 1\)
总之就是各种各样的智障错误啦!
来说这个题目。这个题目厉害就厉害在,你要想到怎么用只有\(20\)个叶子节点这个信息。对于任意一条树上的路径,它一定对应于以某个叶节点为根的树中,一条根节点发出的链上的后缀。既然叶节点只有\(20\)个,我们要做的就是换\(20\)次根就好了。由于是要搞根节点发出链上的后缀,所以我们事实上可以把这颗树当做一个\(Trie\)来使用。这样的话,每次把整颗\(Trie\)插入\(SAM\)中(新学的骚操作\(XD\)),最终求出所有节点的\(len[i] - len[fa[i]]\),就是不同子串个数了。
记得判重。把整颗\(Trie\)插进去的操作真的是学到了。
#include <bits/stdc++.h>
using namespace std;
const int N = 4000010;
struct Suffix_Auto {
int fa[N], len[N], ch[N][10];
int rt, las, cnt;
void Init () {
rt = las = cnt = 1;
memset (ch, 0, sizeof (ch));
memset (fa, 0, sizeof (fa));
memset (len, 0, sizeof (len));
}
int extend (int p, int c) {
if (ch[p][c] != 0) {
//已有节点
int x = ch[p][c];
if (len[x] == len[p] + 1) {
las = x;
} else {
int y = ++cnt; las = y;
len[y] = len[p] + 1;
fa[y] = fa[x];
fa[x] = y; //注意顺序
memcpy (ch[y], ch[x], sizeof (ch[x]));
while (p != 0 && ch[p][c] == x) {
ch[p][c] = y;
p = fa[p];
}
}
} else {
//单纯的插入
int q = ++cnt; las = q;
len[q] = len[p] + 1;
while (p != 0 && ch[p][c] == 0) {
ch[p][c] = q;
p = fa[p]; // 不要忘了这句啊
}
if (p == 0) {
fa[q] = rt;
} else {
int x = ch[p][c];
if (len[x] == len[p] + 1) {
fa[q] = x;
} else {
int y = ++cnt;
fa[y] = fa[x];
fa[x] = fa[q] = y;
len[y] = len[p] + 1;
memcpy (ch[y], ch[x], sizeof (ch[x]));
while (p != 0 && ch[p][c] == x) {
ch[p][c] = y;
p = fa[p];
}
}
}
}
return las;
}
long long get_ans () {
long long ans = 0;
for (int i = 1; i <= cnt; ++i) {
ans += len[i] - len[fa[i]];
}
return ans;
}
}sam;
int n, m, u, v, cnt, d[N], col[N], head[N];
struct edge {
int nxt, to;
}e[N << 1];
void add_edge (int from, int to){
e[++cnt].nxt = head[from];
e[cnt].to = to;
head[from] = cnt;
}
void get_tree (int u, int fa, int p) {
p = sam.extend (p, col[u]); //在p后面接上节点col[u]
for (int i = head[u]; i; i = e[i].nxt) {
int v = e[i].to;
if (v != fa) {
get_tree (v, u, p);
}
}
}
void add_len (int u, int v) {
add_edge (u, v);
add_edge (v, u);
}
int main () {
cin >> n >> m;
sam.Init ();
for (int i = 1; i <= n; ++i) {
cin >> col[i];
}
for (int i = 1; i <= n - 1; ++i) {
cin >> u >> v;
add_len (u, v);
++d[u], ++d[v];
}
for (int i = 1; i <= n; ++i) {
if (d[i] == 1) {
get_tree (i, 0, sam.rt);
//以i为树根跑一遍
}
}
cout << sam.get_ans () << endl;
}
Luogu P3346 [ZJOI2015]诸神眷顾的幻想乡 广义SAM 后缀自动机的更多相关文章
- BZOJ3926:[ZJOI2015]诸神眷顾的幻想乡(广义SAM)
Description 幽香是全幻想乡里最受人欢迎的萌妹子,这天,是幽香的2600岁生日,无数幽香的粉丝到了幽香家门前的太阳花田上来为幽香庆祝生日. 粉丝们非常热情,自发组织表演了一系列节目给幽香看. ...
- 洛谷P3346 [ZJOI2015]诸神眷顾的幻想乡(广义后缀自动机)
题意 题目链接 Sol 广义SAM的板子题. 首先叶子节点不超过20,那么可以直接对每个叶子节点为根的子树插入到广义SAM中. 因为所有合法的答案一定是某个叶子节点为根的树上的一条链,因此这样可以统计 ...
- BZOJ 3926: [Zjoi2015]诸神眷顾的幻想乡 [广义后缀自动机 Trie]
3926: [Zjoi2015]诸神眷顾的幻想乡 Time Limit: 10 Sec Memory Limit: 512 MBSubmit: 1124 Solved: 660[Submit][S ...
- 【BZOJ3926】[Zjoi2015]诸神眷顾的幻想乡 广义后缀自动机
[BZOJ3926][Zjoi2015]诸神眷顾的幻想乡 Description 幽香是全幻想乡里最受人欢迎的萌妹子,这天,是幽香的2600岁生日,无数幽香的粉丝到了幽香家门前的太阳花田上来为幽香庆祝 ...
- P3346 [ZJOI2015]诸神眷顾的幻想乡
思路 注意到叶子节点(度数为1)只有20个,可以分别以这20个节点为根,把所有子串插入SAM中,统计最后的本质不同的子串个数 所以就是广义SAM了 然后注意要判断一下有无重复插入 代码 #includ ...
- [洛谷P3346][ZJOI2015]诸神眷顾的幻想乡
题目大意:给你一棵$n$个点的树,最多有$20$个叶子节点,问共有几个不同的子串 题解:广义$SAM$,对每个叶子节点深搜一次,每个节点的$lst$设为这个节点当时的父亲,这样就可以时建出来的$SAM ...
- bzoj 3926 [Zjoi2015]诸神眷顾的幻想乡(SAM)
[题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=3926 [题意] 给定一棵树,每个节点都有相应的颜色,且保证叶子数不超过20,问 ...
- BZOJ3926 [Zjoi2015]诸神眷顾的幻想乡 字符串 SAM
原文链接https://www.cnblogs.com/zhouzhendong/p/BZOJ3926.html 题目传送门 - BZOJ3926 题意 给定一个有 $n$ 个节点,最多只有 $20$ ...
- BZOJ.3926.[ZJOI2015]诸神眷顾的幻想乡(广义后缀自动机)
题目链接 要对多个串同时建立SAM,有两种方法: 1.将所有串拼起来,中间用分隔符隔开,插入字符正常插入即可. 2.在这些串的Trie上建SAM.实际上并不需要建Trie,还是只需要正常插入(因为本来 ...
随机推荐
- @Html自定义属性
@Html.DropDownList("CardCode", (SelectList)ViewData["cardcodeselectlist"], " ...
- 5.cgroup资源控制
控制组( CGroups)是 Linux 内核的一个特性,主要用来对共享资源进行隔离.限制.审计等. 只有将分配到容器的资源进行控制,才能避免多个容器同时运行时对宿主机系统的资源竞争.每个控制组是一组 ...
- openblas下载安装编译
编译好的库: https://github.com/JuliaLinearAlgebra/OpenBLASBuilder/releases 源码编译 下载:https://github.com/xia ...
- BZOJ3223文艺平衡树——非旋转treap
此为平衡树系列第二道:文艺平衡树您需要写一种数据结构,来维护一个有序数列,其中需要提供以下操作: 翻转一个区间,例如原有序序列是5 4 3 2 1,翻转区间是[2,4]的话,结果是5 2 3 4 1 ...
- #195 game(动态规划+二分)
考虑第一问的部分分.显然设f[i]为i子树从根开始扩展的所需步数,考虑根节点的扩展顺序,显然应该按儿子子树所需步数从大到小进行扩展,将其排序即可. 要做到n=3e5,考虑换根dp.计算某点答案时先将其 ...
- VSIX 插件右键菜单
vs2017 插件开发 环境 WIN10 VS2017 CMMT VSIX 参考资源: vs菜单命令ID速查 https://docs.microsoft.com/zh-cn/visualstudio ...
- 诶西,JavaScript学习记录。。。。。。
由于大学课程缘故,老师巨爱叫人问问题,还记分呢,随便记录一下Js的学习情况,以后复习什么的也比较方便吧...... 开始咯,就按照C语言学习那样的方法来吧! ===================== ...
- LVM-COW写实备份
[root@localhost ~]# fdisk -l /dev/sdb /dev/sdc | grep "LVM"/dev/sdb1 1 9660 77593918+ 8e L ...
- 【Linux】Linux系统中的权限详解
我们linux服务器上有严格的权限等级,如果权限过高导致误操作会增加服务器的风险.所以对于了解linux系统中的各种权限及要给用户,服务等分配合理的权限十分重要. 一.文件基本权限 首先看下linux ...
- 哈尔滨工程大学ACM预热赛 G题 A hard problem(数位dp)
链接:https://ac.nowcoder.com/acm/contest/554/G Now we have a function f(x): int f ( int x ) { if ( ...