ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

求解lca问题

2021-07-18 20:58:30  阅读:157  来源: 互联网

标签:lg return 求解 int 问题 fa depth lca now


文章目录

倍增法

一篇博客
最常用,也是最简单的算法,实质就是直接对暴力使用倍增优化将复杂度降低达到需求。
d e p t h [ ] depth[] depth[]为每个节点的深度, f a [ i ] [ j ] fa[i][j] fa[i][j],i节点的 2 j 2^j 2j的父亲。 l g [ i ] = l o g 2 i + 1 lg[i]=log_2{i}+1 lg[i]=log2​i+1

const int maxn=5000001;
int depth[maxn],fa[maxn][22],lg[maxn];
vector<int >g[maxn];
void dfs(int now,int fath)//dfs初始化fa和depth,传入参数为根节点
{
	fa[now][0]=fath;
	depth[now]=depth[fath]+1;
	for(int i=1;i<=lg[depth[now]];++i)
	{
		fa[now][i]=fa[fa[now][i-1]][i-1];//递推更新fa
	}
	for(int i=0;i<g[now].size();i++)//寻找子节点
	{
		if(g[now][i]!=fath)
		dfs(g[now][i],now);
	}
}
int lca(int x,int y)
{
	if(depth[x]<depth[y])//使x的深度大于y的深度
	{
		swap(x,y);
	}
	while(depth[x]>depth[y])
	{
		x=fa[x][lg[depth[x]-depth[y]]-1];//使两者深度相同
	}
	if(x==y)
	return x;
	for(int k=lg[depth[x]]-1;k>=0;--k)//树上倍增
	{
		if(fa[x][k]!=fa[y][k])
		{
			x=fa[x][k],y=fa[y][k];
		}
	}
	return fa[x][0];
}
main(void)
{
	int n,m,s;
	cin>>n>>m>>s;
	for(int i=1;i<n;i++)
	{
		int x,y;
		scanf("%d%d",&x,&y);
		g[x].push_back(y);
		g[y].push_back(x); 
	}
	for(int i=1;i<=n;i++)
	{
		lg[i]=lg[i-1]+(1<<lg[i-1]==i);
	}
	dfs(s,0);
	for(int i=1;i<=m;i++)
	{
		int x,y;
		scanf("%d%d",&x,&y);
		printf("%d\n",lca(x,y));	
	}
	return 0;
}

题目

二叉树问题
统计树的深度、宽度、两个点之间的上行边数和下行边数

const int maxn=5000001;
int depth[maxn],fa[maxn][22],lg[maxn];
vector<int >g[maxn];
int ans1=0;
int ans2[20000]={0};
int ans22=0;
void dfs(int now,int fath)
{
	fa[now][0]=fath;
	depth[now]=depth[fath]+1;
	ans1=max(ans1,depth[now]);
	ans2[depth[now]]++;
	ans22=max(ans22,ans2[depth[now]]);
	for(int i=1;i<=lg[depth[now]];++i)
	{
		fa[now][i]=fa[fa[now][i-1]][i-1];
	}
	for(int i=0;i<g[now].size();i++)
	{
		if(g[now][i]!=fath)
		dfs(g[now][i],now);
	}
}
int lca(int x,int y)
{
	
	int ans=0;
	int ans11=0;
	int flag=0;
	if(depth[x]<depth[y])
	{
		swap(x,y);
		flag=1;
	}
	while(depth[x]>depth[y])
	{
		ans11+=1<<(lg[depth[x]-depth[y]]-1);
		x=fa[x][lg[depth[x]-depth[y]]-1];
	}
	if(x==y)
	{
		if(flag)
		return ans11;
		return 2*ans11;
	}
	for(int k=lg[depth[x]]-1;k>=0;--k)
	{
		if(fa[x][k]!=fa[y][k])
		{
			x=fa[x][k],y=fa[y][k];
			ans+=1<<k;
			ans11+=1<<k;
		}
	}
	if(flag)
	return (ans+1)*2+(ans11+1);
	return (ans+1)+(ans11+1)*2;
}
main(void)
{
	int n,m,s=1,u,v;
	cin>>n;
	for(int i=1;i<n;i++)
	{
		int x,y;
		scanf("%d%d",&x,&y);
		g[x].push_back(y);
		g[y].push_back(x); 
	}
	for(int i=1;i<=n;i++)
	{
		lg[i]=lg[i-1]+(1<<lg[i-1]==i);
	}
	dfs(s,0);
	cin>>u>>v;
	printf("%d\n%d\n%d\n",ans1,ans22,lca(u,v));
	return 0;
}

P2420 让我们异或吧
运用lca中的dfs,求得所有点到根的异或

const int maxn=5000001;
int depth[maxn],fa[maxn][22],lg[maxn];
vector<int >g[maxn];
int ans1=0;
int ans2[20000]={0};
int ans22=0;
void dfs(int now,int fath)
{
	fa[now][0]=fath;
	depth[now]=depth[fath]+1;
	ans1=max(ans1,depth[now]);
	ans2[depth[now]]++;
	ans22=max(ans22,ans2[depth[now]]);
	for(int i=1;i<=lg[depth[now]];++i)
	{
		fa[now][i]=fa[fa[now][i-1]][i-1];
	}
	for(int i=0;i<g[now].size();i++)
	{
		if(g[now][i]!=fath)
		dfs(g[now][i],now);
	}
}
int lca(int x,int y)
{
	
	int ans=0;
	int ans11=0;
	int flag=0;
	if(depth[x]<depth[y])
	{
		swap(x,y);
		flag=1;
	}
	while(depth[x]>depth[y])
	{
		ans11+=1<<(lg[depth[x]-depth[y]]-1);
		x=fa[x][lg[depth[x]-depth[y]]-1];
	}
	if(x==y)
	{
		if(flag)
		return ans11;
		return 2*ans11;
	}
	for(int k=lg[depth[x]]-1;k>=0;--k)
	{
		if(fa[x][k]!=fa[y][k])
		{
			x=fa[x][k],y=fa[y][k];
			ans+=1<<k;
			ans11+=1<<k;
		}
	}
	if(flag)
	return (ans+1)*2+(ans11+1);
	return (ans+1)+(ans11+1)*2;
}
main(void)
{
	int n,m,s=1,u,v;
	cin>>n;
	for(int i=1;i<n;i++)
	{
		int x,y;
		scanf("%d%d",&x,&y);
		g[x].push_back(y);
		g[y].push_back(x); 
	}
	for(int i=1;i<=n;i++)
	{
		lg[i]=lg[i-1]+(1<<lg[i-1]==i);
	}
	dfs(s,0);
	cin>>u>>v;
	printf("%d\n%d\n%d\n",ans1,ans22,lca(u,v));
	return 0;
}

疫情控制
要求分配军队,运用了lca的dfs,得到每个点的祖先节点情况。

const int N=5e4+3;
ll sum,dis[N],dist[N];
int n,m,u,cnt,cont;
int am[N],lg[N],f[N][17],dp[N],vis[N],fre[N],b[N],pl[N],nee[N];
int tt,head[N],to[N<<1],nex[N<<1],w[N<<1];
vector<int>arv[N];
struct LCA{//直接套的lca模板
	void pre(int g,int F){//预处理父亲;
		for(int i=head[g],v;i;i=nex[i]){
			v=to[i];
			if(v==F)continue;
			f[v][0]=g,dp[v]=dp[g]+1,dis[v]=dis[g]+(ll)w[i];
			for(int j=1;j<=lg[dp[v]];++j)
				f[v][j]=f[f[v][j-1]][j-1];
			pre(v,g);
		}
		return ;
	}
	int get(int x,int y){
		if(dp[x]>dp[y])swap(x,y);
		for(int i=lg[dp[y]-dp[x]];i>=0;--i)
			if(dp[f[y][i]]>=dp[x])y=f[y][i];
		if(x==y)return x;
		for(int i=lg[dp[x]];i>=0;--i)
			if(f[x][i]!=f[y][i])x=f[x][i],y=f[y][i];
		return f[x][0];
	}
}lca;
inline void add(int x,int y,int W){
	to[++tt]=y,nex[tt]=head[x],w[tt]=W,head[x]=tt;
	return ;
}
inline bool dfs(int g,int F){
	if(vis[g]&&!pl[g])return true;//若当前节点不是根节点的子节点,且有标记,说明当前子树已全部被标记;
	bool flag=0;
	for(int i=head[g],v;i;i=nex[i]){
		v=to[i];
		if(v==F)continue;
		if(!dfs(v,g))return false;//有一个叶子节点未被标记就需要加入need集合;
		flag=1;
	}
	return flag;
}
inline bool check(ll mid){
	for(int i=1;i<=cnt;++i)arv[i].clear();
	memset(vis,0,sizeof vis);
	for(int i=1;i<=m;++i){
		int y=am[i];
		dist[i]=0;
		for(int j=dp[y];j>=0;--j)//;将其向父亲跳
			if(f[y][j]>1&&dist[i]+dis[y]-dis[f[y][j]]<=mid){
				dist[i]+=dis[y]-dis[f[y][j]];
				y=f[y][j];
			}
		vis[y]=1;//将当前节点标记;
		int j=pl[y];//找到对应的根节点的子节点的编号;
		if(j){
			arv[j].push_back(mid-dist[i]);//将剩余距离加入第j个子节点;
			if(arv[j].size()>1&&arv[j][arv[j].size()-2]<arv[j][arv[j].size()-1])
				swap(arv[j][arv[j].size()-2],arv[j][arv[j].size()-1]);//将剩余距离最短的点放到尾部;
		}
	}
	int cnt1=0,cnt2=0;
	for(int i=1;i<=cnt;++i){
		if(!dfs(to[b[i]],1)){//判定是否需要军队驻扎;
			if(arv[i].size()&&arv[i][arv[i].size()-1]<w[b[i]]*2)
				arv[i].pop_back();//若当前点存在无法先到达根节点,再返回当前点的的军队,就将其军队用来驻扎当前点;
			else nee[++cnt1]=w[b[i]];//否则将当前点加入need集合;
		}
		for(int j=0;j<arv[i].size();++j)
			if(arv[i][j]>=w[b[i]])
				fre[++cnt2]=arv[i][j]-w[b[i]];//将符合条件的军队加入free集合;
	}
	if(cnt1>cnt2)return false;//如果军队数小于节点数,就无法完全覆盖;
	sort(nee+1,nee+1+cnt1);
	sort(fre+1,fre+1+cnt2);//排序;
	while(cnt1){//反向比较;
		if(fre[cnt2]<nee[cnt1])return false;//若当前点无法被当前军队覆盖,那么在他之后的军队也无法将其覆盖;
		--cnt2,--cnt1;
	}
	return true;//将节点完全覆盖;
}
inline ll solve(){
	ll l=0,r=sum+1;
	while(l<r){
		ll mid=l+r>>1;
		if(check(mid))r=mid;
		else l=mid+1;
	}
	return l;
}
int main(){
	cin>>n;
	lg[0]=-1;
	for(int i=1;i<=n;++i)lg[i]=lg[i>>1]+1;//初始化lg数组;
	int x,y,W;
	for(int i=1;i<n;++i)cin>>x>>y>>W,add(x,y,W),add(y,x,W),sum+=W;
	for(int i=head[1],v;i;i=nex[i])v=to[i],b[++cnt]=i,pl[v]=cnt;//b记录根节点的第cnt条边,pl记录节点对应第几条边;
	cin>>m;
	lca.pre(1,0);
	for(int i=1;i<=m;++i)cin>>x,am[i]=x;
	if(m<cnt)puts("-1");//若军队数小于根节点的子节点数,则一定无法完全覆盖;
	else cout<<solve()<<'\n';//否则一定可以完全覆盖;
	return 0;
}

标签:lg,return,求解,int,问题,fa,depth,lca,now
来源: https://blog.csdn.net/csdn_ggboy/article/details/107068661

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有