题目链接 \(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 后缀自动机的更多相关文章

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

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

  2. 洛谷P3346 [ZJOI2015]诸神眷顾的幻想乡(广义后缀自动机)

    题意 题目链接 Sol 广义SAM的板子题. 首先叶子节点不超过20,那么可以直接对每个叶子节点为根的子树插入到广义SAM中. 因为所有合法的答案一定是某个叶子节点为根的树上的一条链,因此这样可以统计 ...

  3. BZOJ 3926: [Zjoi2015]诸神眷顾的幻想乡 [广义后缀自动机 Trie]

    3926: [Zjoi2015]诸神眷顾的幻想乡 Time Limit: 10 Sec  Memory Limit: 512 MBSubmit: 1124  Solved: 660[Submit][S ...

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

    [BZOJ3926][Zjoi2015]诸神眷顾的幻想乡 Description 幽香是全幻想乡里最受人欢迎的萌妹子,这天,是幽香的2600岁生日,无数幽香的粉丝到了幽香家门前的太阳花田上来为幽香庆祝 ...

  5. P3346 [ZJOI2015]诸神眷顾的幻想乡

    思路 注意到叶子节点(度数为1)只有20个,可以分别以这20个节点为根,把所有子串插入SAM中,统计最后的本质不同的子串个数 所以就是广义SAM了 然后注意要判断一下有无重复插入 代码 #includ ...

  6. [洛谷P3346][ZJOI2015]诸神眷顾的幻想乡

    题目大意:给你一棵$n$个点的树,最多有$20$个叶子节点,问共有几个不同的子串 题解:广义$SAM$,对每个叶子节点深搜一次,每个节点的$lst$设为这个节点当时的父亲,这样就可以时建出来的$SAM ...

  7. bzoj 3926 [Zjoi2015]诸神眷顾的幻想乡(SAM)

    [题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=3926   [题意]   给定一棵树,每个节点都有相应的颜色,且保证叶子数不超过20,问 ...

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

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

  9. BZOJ.3926.[ZJOI2015]诸神眷顾的幻想乡(广义后缀自动机)

    题目链接 要对多个串同时建立SAM,有两种方法: 1.将所有串拼起来,中间用分隔符隔开,插入字符正常插入即可. 2.在这些串的Trie上建SAM.实际上并不需要建Trie,还是只需要正常插入(因为本来 ...

随机推荐

  1. How to install rime on Debian

    apt-get install ibus ibus-rime librime-data-wubi reboot cp ~/.config/ibus/rime/default.yaml ~/.confi ...

  2. Razor Pages with ASP.NET Core 2

    With ASP.NET Core 2 we get another way of building web applications. It’s one of those new things th ...

  3. servlet篇 之 生命周期

    二:Servlet的生命周期 背景知识: servlet是单例,在web项目运行期间,一个servlet只会创建一个对象[tomcat帮我们实例 化][尽量不要在servlet中定义成员变量].因为w ...

  4. 常用模块collections

    collections模块 在内置数据类型(dict.list.set.tuple)的基础上,collections模块还提供了几个额外的数据类型:Counter.deque.defaultdict. ...

  5. LIRE图片识别搜索demo--Ubuntu开发

    Ubuntu安装shadowsocks客户端/服务端教程 1.安装shadowsocks sudo apt-get update sudo apt-get install python-pip sud ...

  6. Android Spinner 绑定键值对

    这里给大家提供下绑定 spinner键值对的方法. 首先创建绑定模型BaseItem public class BaseItem { public BaseItem(Integer id,String ...

  7. Elasticsearch 5.x 字段折叠的使用

    在Elasticsearch 5.x  之前,如果实现一个数据折叠的功能是非常复杂的,随着5.X的更新,这一问题变得简单,找到了一遍技术文章,对这个问题描述的非常清楚,收藏下. 参考:https:// ...

  8. hdu 2191 (多重背包二进制优化)

    链接:http://acm.hdu.edu.cn/showproblem.php?pid=2191 实现代码: #include<bits/stdc++.h> using namespac ...

  9. php插入日志到数据库,对象转json

    打印插入日志数据到库 M()->table("t_log")->data(array( 'id'=>'6'.time(), 't'=> json_encod ...

  10. Apache Ignite上的TensorFlow

    任何深度学习都是从数据开始的,这是关键点.没有数据,就无法训练模型,也无法评估模型质量,更无法做出预测,因此,数据源非常重要.在做研究.构建新的神经网络架构.以及做实验时,会习惯于使用最简单的本地数据 ...