【AC自动机 可持久化线段树】魔法串(2022.5.28)

有一些算法,懂了的人明了于心,以一言为多余,不懂的人怎么也不懂...

题目

3 魔法串(magic.c/cpp/pas)

3.1 题目描述

给你一棵 n + 1 个结点的有根树,结点从 0 到 n 标号,其中 0 为根结点。
这是一棵魔法树。这棵树的每条边有一个魔力值,同一个结点连向不同子结点的边的魔
力值不同。一个结点所代表的魔法串是从根一直走到这个结点,经过的魔力值依次排列形成
的有序序列,另外,一个串是魔法串当且仅当它被一个结点所代表。
现在,为了使用强大的魔法,你需要对每个魔法串,找到最长的是它后缀的魔法串。为
了方便输出,你只需要输出代表这个魔法串的结点的标号即可。若没有这个魔法串,请输出 0。

3.2 输入格式

第一行一个整数 n,代表除根以外的结点个数。
第二行 n 个整数,第 i 个整数 Pi 代表标号为 i 的结点的父亲标号。
第三行 n 个整数,第 i 个整数 Ci 代表标号为 i 的结点连向父亲的边的魔力值

3.3 输出格式

输出一行 n 个整数,第 i 个整数表示第 i 个结点代表的魔法串的答案。

3.4 样例输入

 7 
0 0 1 1 2 4 5 
1 2 3 2 1 1 3 

3.5 样例输出

0 0 0 2 1 5 3 

3 .6 数据范围与约定

对于前30%的数据,n≤2000;
对于100%的数据,1≤n≤200000, 0≤Pi<i, 1≤Ci≤n。

解思

(既然被人说题解太简陋,那我还是写详细一点吧)

前置知识:AC自动机不会的建议先学一下,这个知识非常重要!

会AC自动机的同学,大概一眼就能看出来此题求最长后缀的点明显是求AC自动机中的fail指针。

于是我就自信地写了个AC自动机的板子套了个unordered_map交上去(然后发现空间炸了)

首先这题是肯定需要补全AC自动机的(把fail指针化做边,直接从当前点的fail点继承fail点的子儿子)

重新审视一下数据范围,200000的范围意味着需要一个logn查找当前点连向的点是否有权值为Ci的点,同时又需要继承之前的状态,考虑可持久化线段树维护。转移边最多只有n条,所以最多进行n次转移,一共复杂度O(nlogn)

于是乎就可以过了...

上代码!


#include<bits/stdc++.h>
using namespace std;
inline int read(){
	int f=1,j=0;char w=getchar();
	while(w>'9'||w<'0'){
		if(w=='-')f=-1;
		w=getchar();
	}
	while(w>='0'&&w<='9'){
		j=(j<<3)+(j<<1)+w-'0';
		w=getchar();
	}
	return f*j;
} 
const int N=200001;
int n,fa[N],sum[N],f[N],depth[N];
int root[N],ls[N*20],rs[N*20],val[N*20],tail_t;
int head[N],to[N],front[N],tail;
bool use[N];
void addline(int x,int y){
	to[++tail]=y;
	front[tail]=head[x];
	head[x]=tail;
	return ;
}
int find(int nown,int l,int r,int aim){
	if(nown==0)return 0;
	if(l==r)return val[nown];
	int mid=(l+r)/2;
	if(aim<=mid)return find(ls[nown],l,mid,aim);
	else return find(rs[nown],mid+1,r,aim);
}
void inherit(int x,int &y,int l,int r,int aim,int w){
	y=++tail_t;
	if(l==r){
		val[y]=w;
		return ;
	}
	int mid=(l+r)/2;
	if(aim<=mid)rs[y]=rs[x],inherit(ls[x],ls[y],l,mid,aim,w);
	else ls[y]=ls[x],inherit(rs[x],rs[y],mid+1,r,aim,w);
	return ;
}
void work(){
	deque<int>line,Q;
	int now_dep=2;
	for(int k=head[0];k;k=front[k]){
		int x=to[k];
		depth[x]=1;
		inherit(root[0],root[0],1,n,sum[x],x);
		for(int a=head[x];a;a=front[a]){
			int b=to[a];
			depth[b]=2;
			line.push_back(b);
		}
	}
	for(int k=head[0];k;k=front[k])root[to[k]]=root[0];
	while(!line.empty()){
		int nown=line.front();line.pop_front();
		if(depth[nown]!=now_dep){
			now_dep++;
			while(!Q.empty())root[Q.front()]=root[f[Q.front()]],Q.pop_front();
		}
		Q.push_back(nown);
		f[nown]=find(root[fa[nown]],1,n,sum[nown]);
		inherit(root[fa[nown]],root[fa[nown]],1,n,sum[nown],nown);
		for(int k=head[nown];k;k=front[k]){
			int x=to[k];
			depth[x]=depth[nown]+1;
			line.push_back(x);
		}
	}
	return ;
}
signed main(){
	//freopen("magic.in","r",stdin);
	//freopen("magic.out","w",stdout);
	n=read();
	for(int i=1;i<=n;i++){
		fa[i]=read();
		addline(fa[i],i);
	}
	for(int i=1;i<=n;i++)sum[i]=read();
	work();
	for(int i=1;i<=n;i++)printf("%d ",f[i]);
	return 0;
}
posted @   flywatre  阅读(70)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示