Ancestor 题解 (倍增算法)

 

目录

 

题目链接:

题目大意:

算法:

想法:

倍增算法预处理:

倍增算法求公共祖先:

题解:


题目链接:

​​​​​​A-Ancestor_"蔚来杯"2022牛客暑期多校训练营3 (nowcoder.com)

题目大意:

先给一组序列,再给a,b两个树,分别给两行,一行是n个树点的权值,一行是n-1个树点的父亲节点。要求:序列中去掉一个点后其余点在两树的公共祖先的权值进行比较,a大就ans++。求ans的大小。(即去掉点遍历)。

算法:

倍增算法,dfs。

想法:

预处理出关键点序列的在树A B上的前缀LCA和后缀LCA,枚举去掉的关键节点并使用前后缀LCA算出剩余节点的LCA比较权值。

但是朴素求祖先会超时,这时候就需要倍增算法。

倍增算法预处理:

void dfs(int x,int fath,int dep){
	fa[x][0]=fath;
	depth[x]=dep+1;
	for (int i=1;(1<<i)<=dep+1;i++){
		fa[x][i]=fa[fa[x][i-1]][i-1];//预处理x的第2^i个祖先
	}
	int len=v[x].size();
	for (int i=0;i<len;i++){	
		if (v[x][i]!=fath){
			dfs(v[x][i],x,dep+1);//常规dfs
		}
	}
}

倍增算法求公共祖先:

int LCA(int p,int q){//让pq深度变成相同一次倍增,pq一起动一次倍增
	if (a.depth[p]<a.depth[q]) swap(p,q);
	nep(i,29,0){//倒序使用倍增是精髓
		if (a.depth[p]>=a.depth[q]+(1<<i)) 
			p=fa[p][i];
	}
	if (p==q) return p;
	nep(i,29,0){
		if (fa[p][i]!=fa[q][i]){
			p=fa[p][i];
			q=fa[q][i];
		}
	}
	return fa[p][0];
}

题解:

#include <bits/stdc++.h>
#define endl '\n'
#define int long long
#define pii pair<int,int>
#define rep(i,l,r) for (int i=l;i<=r;i++)
#define nep(i,r,l) for (int i=r;i>=l;i--)
#define CIO std::ios::sync_with_stdio(false)
using namespace std;
const int N=2e5+5;
int x[N];
int lca[N],lda[N],lcb[N],ldb[N];
int fa[N][30],fb[N][30];
vector<int> v[N];
struct shu{
	int val[N];
	int pp[N];
	int depth[N];
}a,b;
void dfs1(int x,int fath,int dep){
	fa[x][0]=fath;
	a.depth[x]=dep+1;
	for (int i=1;(1<<i)<=dep+1;i++){
		fa[x][i]=fa[fa[x][i-1]][i-1];
	}
	int len=v[x].size();
	for (int i=0;i<len;i++){	
		if (v[x][i]!=fath){
			dfs1(v[x][i],x,dep+1);
		}
	}
}
void dfs2(int x,int fath,int dep){
	fb[x][0]=fath;
	b.depth[x]=dep+1;
	for (int i=1;(1<<i)<=dep+1;i++){
		fb[x][i]=fb[fb[x][i-1]][i-1];
	}
	int len=v[x].size();
	for (int i=0;i<len;i++){	
		if (v[x][i]!=fath){
			dfs2(v[x][i],x,dep+1);
		}
	}
}
int LCA(int p,int q){
	if (a.depth[p]<a.depth[q]) swap(p,q);
	nep(i,29,0){
		if (a.depth[p]>=a.depth[q]+(1<<i)) 
			p=fa[p][i];
	}
	if (p==q) return p;
	nep(i,29,0){
		if (fa[p][i]!=fa[q][i]){
			p=fa[p][i];
			q=fa[q][i];
		}
	}
	return fa[p][0];
}
int LCB(int p,int q){
	if (b.depth[p]<b.depth[q]) swap(p,q);
	nep(i,29,0){
		if (b.depth[p]>=b.depth[q]+(1<<i)) 
			p=fb[p][i];
	}
	if (p==q) return p;
	nep(i,29,0){
		if (fb[p][i]!=fb[q][i]){
			p=fb[p][i];
			q=fb[q][i];
		}
	}
	return fb[p][0];
}
void work(){
	int n,k;
	cin>>n>>k;
	rep(i,1,k) cin>>x[i];
	rep(i,1,n) cin>>a.val[i];
	rep(i,2,n){
		cin>>a.pp[i];
		v[a.pp[i]].push_back(i);
	}
	dfs1(1,-1,-1);
	lca[1]=x[1];
	rep(i,2,k){
		lca[i]=LCA(lca[i-1],x[i]);
	}
	lda[k]=x[k];
	nep(i,k-1,1){
		lda[i]=LCA(lda[i+1],x[i]);
	}
	rep(i,1,n){
		v[i].clear();
	}
	rep(i,1,n) cin>>b.val[i];
	rep(i,2,n){
		cin>>b.pp[i];
		v[b.pp[i]].push_back(i);
	}
	dfs2(1,-1,-1);
	lcb[1]=x[1];
	rep(i,2,k){
		lcb[i]=LCB(lcb[i-1],x[i]);
	}
	ldb[k]=x[k];
	nep(i,k-1,1){
		ldb[i]=LCB(ldb[i+1],x[i]);
	}
	int ans=0;
	if (a.val[lda[2]]>b.val[ldb[2]]){
		ans++;
	}
	rep(i,2,k-1){
		int aa=LCA(lca[i-1],lda[i+1]);
		int bb=LCB(lcb[i-1],ldb[i+1]);
		if (a.val[aa]>b.val[bb]){
			ans++;
		}
	}
	if (a.val[lca[k-1]]>b.val[lcb[k-1]]){
		ans++;
	}
	cout<<ans<<endl;
}
signed main(){
	CIO;
	{
		work();
	}
	return 0;
}

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

繁水682

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值