练习赛 魔法串 (补全AC自动机 可持久化树)
来源:互联网 发布:中国农业网络书屋 编辑:程序博客网 时间:2024/05/19 15:43
魔法串
题目描述
给你一棵n+1个结点的有根树,结点从0到n标号,其中0为根结点。
这是一棵魔法树。这棵树的每条边有一个魔力值,同一个结点连向不同子结点的边的魔力值不同。一个结点所代表的魔法串是从根一直走到这个结点,经过的魔力值依次排列形成的有序序列,另外,一个串是魔法串当且仅当它被一个结点所代表。
现在,为了使用强大的魔法,你需要对每个魔法串,找到最长的是它后缀的魔法串。为了方便输出,你只需要输出代表这个魔法串的结点的标号即可。若没有这个魔法串,请输出0。
输入格式
第一行一个整数n,代表除根以外的结点个数。
第二行 n个整数,第i个整数P_i代表标号为i的结点的父亲标号。
第三行 n个整数,第i个整数C_i代表标号为i的结点连向父亲的边的魔力值。
输出格式
输出一行n个整数,第i个整数表示第i个结点代表的魔法串的答案。
样例输入
7
0 0 1 1 2 4 5
1 2 3 2 1 1 3
样例输出
0 0 02 1 5 3
数据范围与约定
对于30%的数据,保证1<=n<=2000。
对于100%的数据,保证1<=n<=200000,0<=P _ i < i,1<=C _ i<=n。
思路:
前30%的数据:考虑AC自动机的思路,我们要求的答案相当于是求每个点的fail。直接用AC自动机做,用map存储转移数组。
前100%的数据:上一个方法之所以T掉,是因为我们找fail的时候如果找不到会继续跳fa的fail,而这里C _ i<=n,也就是说一个点的子节点由26变为了200000,炸飞!
于是补全AC自动机(Trie图),考虑一个结点u所连出的转移边与fail[u]所连出的转移边的关系,只有u直接连出的边会影响这些转移边,而边数是n-1条。于是我们考虑将fail[u]的转移边全部复制给u,再在此基础上对u的转移边进行修改。
这个如何实现?用可持久化线段树维护每个结点的转移边即可。
#include<cstdio>#include<cstring>#include<algorithm>using namespace std;#define N 200010#define M 6000010int fail[N], q[N], root[N], fat[N], c[N], head[N];int idc=0, tot, n;struct Edge {int next, to, w;}ed[N];struct Node {int l, r, to;}t[M];void adde(int u, int v, int w){ ed[++idc].to = v; ed[idc].next = head[u]; ed[idc].w = w; head[u] = idc; }void modify(int &x, int l, int r, int val, int v){ int y=x; x=++tot; t[x] = t[y];//新开一个树节点,只有root~val的一条链修改了,其他的就指向fail的等位 //相当于每次修改都只新开了一条链的空间,其他不变的就指向fail的等位 if(l == r){ t[x].to = v;//保存此点在原树中的位置,使得找fail时能快速返回它的位置(代表这个魔法串的结点的标号) return;//直接覆盖更新为最近的 } int mid = (l + r) >> 1; if(val <= mid) return modify(t[x].l, l, mid, val, v);//按照魔法值查找 else return modify(t[x].r, mid+1, r, val, v);}int query(int x, int l, int r, int val){ if(l == r) return t[x].to; int mid = (l + r) >> 1; if(val <= mid) return query(t[x].l, l, mid, val); return query(t[x].r, mid+1, r, val);}int main(){ freopen("magic.in", "r", stdin); freopen("magic.out", "w", stdout); scanf("%d", &n); for(int i=1; i<=n; i++) scanf("%d", &fat[i]); for(int i=1; i<=n; i++) scanf("%d", &c[i]), adde(fat[i], i, c[i]); //用给出的关系可以直接建一棵树,以魔法值作为点权(类似AC自动机) int l=0, r=0; q[r++] = 0;//0为根节点 for(; l<r; l++){//bfs补全找fail int u = q[l]; root[u] = root[fail[u]];//若无子节点就跟fail相同 for(int i=head[u]; i; i=ed[i].next){ fail[q[r++] = ed[i].to] = query(root[fail[u]], 1, n, ed[i].w);//fail[u]已经补全了(有所有子节点) //在root[fail[u]]引领的值域线段树(魔法值为下标,维护在原树中的位置)中查找ed[i].w。 //找到之后得到地址t[x].to(因为补全了所以一定能找到) //t[x].to存的是这个值在原树上的位置(因为是bfs更新的所以存的一定是最近的是它后缀的位置,也就是fail) modify(root[u], 1, n, ed[i].w, ed[i].to);//现在就来更新这棵树(本来root[u] = root[fail[u]]是同一棵树) //遍历它所有真实的子节点,(每一个都有可能成为其他点的fail) //这样的话我们在找u的fail的时候,先找到了fa[u].fail,然后在它的值域线段树中查询与u相等的节点的位置 //如果fa[u].fail真实有这个节点自然就找到了(所有真实子节点都存在树中) //如果fa[u].fail并没有这个节点那么我们找到的就是fa[u].fail.fail...若干个fail的相同子节点的位置 //这样就解决了不能一次找到fail而超时的问题(可以一次找到) } } for(int i=1; i<=n; i++) printf("%d ", fail[i]); return 0;}
- 练习赛 魔法串 (补全AC自动机 可持久化树)
- YYR字符串 魔法串 [Trie图][Fail树][主席树][补全AC自动机]
- 2017.7.26 机房测试(KMP/Hash,分块+可持久化Trie树贪心,主席树+AC自动机)
- 机房测试 字符串 【Hash+KMP】【分块+可持久化Trie树+贪心】【AC自动机+主席树】
- [codeforces 86C]补全AC自动机上DP
- BZOJ1030 [JSOI2007]文本生成器 补全AC自动机+简单DP
- BZOJ2938 POI2000 病毒 补全AC自动机 Trie图判环
- [BZOJ1559]-[JSOI2009]密码-补全AC自动机+状压dp
- [达成成就:Tjoi2016&Heoi2016全AC] bzoj 4556: [Tjoi2016&Heoi2016]字符串 后缀数组+可持久化线段树
- [模板练习]AC自动机
- BZOJ 2434 阿狸的打字机 补全AC自动机 ( Trie图 ) fail树 树状数组 DFS序列
- 【BZOJ2938】病毒,AC自动机练习
- 【BZOJ3172】单词,AC自动机练习
- hdu 2222 AC自动机(可做模板)
- 2017补全计划-持久化缓存-学习笔记
- 自动机理论(AC自动机)
- AC-自动机(AC-Automachine)
- HDU2222 多串匹配 (AC自动机)
- 递归与循环
- 34:回文子串
- Qt,Qt Creator,minGW,VS关系
- Tempter of the Bone
- 华容道!
- 练习赛 魔法串 (补全AC自动机 可持久化树)
- 76
- 77
- 78
- 79
- HttpClient4.3 连接管理
- 80
- 81
- 82