zoukankan      html  css  js  c++  java
  • P3379 最近公共祖先(LCA) 洛谷

    题意简单明了(这就是个模板)。

    就是让我们找2个节点的公共祖先而已,但我们要讲的做法不是生硬的爆搜,而且直接搜好像过不去……

    这次就讲我往后拖了n多天才开始学了倍增LCA。

    嗯,这个题,如果2个节点的深度是不一样的,我们要把他们的深度变成一样的,变成一样的以后就开始倍增搜索。

    上面的这句话为我们点明了这个题的解题方法,我们要求出每个节点的深度,每个节点2的几次方个祖先是谁,还有这棵树是什么样子的(一开始我把第一个输入的当成爸爸,wa的老惨了)。

    首先第一步:创建这颗树

    题目中给出了树的根节点,所以说,和根节点连接的就是第二层的节点,和第二次节点连接的就是第三层的节点……和第n-1层连接的就是第n层的节点。

    虽然这个的实现很简单,但如果有同学不会,可以戳这里

    树建完了,开始第二步:查找一个节点第2的i次方个祖先是谁?

    有个神奇的东西叫做RMQ,和这个差不多,懂了可以去秒一下。

     我们可以通过一个生活实例来思考,比如你爸爸就是你第2^0个祖先,你第2^1的祖先就是你爸爸的爸爸,你的第2^2个祖先就是你爷爷的爷爷……

    可以发现,我们要求的东西可以通过已知的东西获得,这就避免了一些奇怪的模拟。

    现在树建完了,每个子节点的祖先也知道了,接下来就是倍增模拟了:

     就像上面说的一样,先把2个位置统一公共祖先,然后寻找。

    思路都讲完了,直接放代码了:

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cmath>
    #include<cstring>
    using namespace std;
    long long n,m,s,a,b;
    long long cf[100],wz,shu=1;
    long long lca[500005][30],sd[500005];
    long long ans,head[500005];
    struct hehe
    {
    	long long nxt,wei;
    }sz[1000005];
    int add(int tou,int wei)//链式前向星真香 
    {
    	ans++;
    	sz[ans].wei=wei;
    	sz[ans].nxt=head[tou];
    	head[tou]=ans; 
    }
    int dfs(int qd,int bb)//搜深度的地方 
    {
    	lca[qd][0]=bb;
    	for(int i=1;(1<<i)<=sd[qd];i++)//没必要就不搜了,很明白的道理嘛 
    	{
    		lca[qd][i]=lca[lca[qd][i-1]][i-1];//想想祖孙三代的例子或许就明白这句话了。
    	}
    	for(int i=head[qd];i!=0;i=sz[i].nxt)//链式前向星的查找 
    	{
    		if(sz[i].wei!=bb)//因为不知道谁是谁的爸爸,所以要特判一下。 
    		{
    			sd[sz[i].wei]=sd[qd]+1;
    			dfs(sz[i].wei,qd);
    		}
    	}
    }
    long long start(long long x,long long y)
    {
    	if(sd[x]<sd[y])//永远让x的深度 
    	{
    		swap(x,y);
    	}
    	int i=0;
    	while((1<<i)<=sd[x])
    	{
    		i++;
    	}
    	i--;
    	if(sd[x]!=sd[y])//不等于就大步的往前走 
    	{
    		for(int j=i;j>=0;j--)
    		{
    			if(sd[lca[x][j]]>=sd[y])//小心不要走过。 
    			{
    				x=lca[x][j];
    			}
    		}	
    	}
    	if(x==y)//x是y的子节点呢,没事了,直接输出。 
    	{
    		return x;
    	}
    	for(int j=i;j>=0;j--)
    	{
    		if(lca[x][j]!=lca[y][j])//这个依旧不要走过 
    		{
    			x=lca[x][j];
    			y=lca[y][j];
    		}
    	}
    	return lca[x][0];//因为倍增的奇妙,最后一定会停留在答案的下一层,上升一层就可以了。
    }
    int main()
    {
    	scanf("%lld%lld%lld",&n,&m,&s);//正常的读入
    	sd[s]=1;
    	lca[s][0]=0;
    	for(int i=0;i<n-1;i++)
    	{
    		scanf("%lld%lld",&a,&b);
    		add(a,b);
    		add(b,a);
    	}
    	dfs(s,0);
    	for(int i=0;i<m;i++)
    	{
    		scanf("%lld%lld",&a,&b);
    		printf("%lld
    ",start(a,b));//正常的运算和输出
    	}
    	return 0;
    }
    

    这个题就到这里吧,讲完了。

  • 相关阅读:
    白话经典算法系列之六 高速排序 高速搞定
    簡單SQL存儲過程實例
    ACE编译运行错误解决
    shell之变量与read
    Java实现第八届蓝桥杯魔方状态
    Java实现第八届蓝桥杯魔方状态
    Java实现第八届蓝桥杯魔方状态
    Java实现第八届蓝桥杯魔方状态
    Java实现第八届蓝桥杯购物单
    Java实现第八届蓝桥杯购物单
  • 原文地址:https://www.cnblogs.com/lichangjian/p/12863983.html
Copyright © 2011-2022 走看看