hihoCoder #1069 : 最近公共祖先·三(DFS+LCA,倍增,裸题)

题目链接:http://hihocoder.com/problemset/problem/1069

时间限制:10000ms

单点时限:1000ms

内存限制:256MB

描述

上上回说到,小Hi和小Ho使用了Tarjan算法来优化了他们的“最近公共祖先”网站,但是很快这样一个离线算法就出现了问题:如果只有一个人提出了询问,那么小Hi和小Ho很难决定到底是针对这个询问就直接进行计算还是等待一定数量的询问一起计算。毕竟无论是一个询问还是很多个询问,使用离线算法都是只需要做一次深度优先搜索就可以了的。

那么问题就来了,如果每次计算都只针对一个询问进行的话,那么这样的算法事实上还不如使用最开始的朴素算法呢!但是如果每次要等上很多人一起的话,因为说不准什么时候才能够凑够人——所以事实上有可能要等上很久很久才能够进行一次计算,实际上也是很慢的!

“那到底要怎么办呢?在等到10分钟,或者凑够一定数量的人两个条件满足一个时就进行运算?”小Ho想出了一个折衷的办法。

“哪有这么麻烦!别忘了和离线算法相对应的可是有一个叫做在线算法的东西呢!”小Hi笑道。

小Ho面临的问题还是和之前一样:假设现在小Ho现在知道了N对父子关系——父亲和儿子的名字,并且这N对父子关系中涉及的所有人都拥有一个共同的祖先(这个祖先出现在这N对父子关系中),他需要对于小Hi的若干次提问——每次提问为两个人的名字(这两个人的名字在之前的父子关系中出现过),告诉小Hi这两个人的所有共同祖先中辈分最低的一个是谁?

提示:最近公共祖先无非就是两点连通路径上高度最小的点嘛!

输入

每个测试点(输入文件)有且仅有一组测试数据。

每组测试数据的第1行为一个整数N,意义如前文所述。

每组测试数据的第2~N+1行,每行分别描述一对父子关系,其中第i+1行为两个由大小写字母组成的字符串Father_i, Son_i,分别表示父亲的名字和儿子的名字。

每组测试数据的第N+2行为一个整数M,表示小Hi总共询问的次数。

每组测试数据的第N+3~N+M+2行,每行分别描述一个询问,其中第N+i+2行为两个由大小写字母组成的字符串Name1_i, Name2_i,分别表示小Hi询问中的两个名字。

对于100%的数据,满足N<=10^5,M<=10^5, 且数据中所有涉及的人物中不存在两个名字相同的人(即姓名唯一的确定了一个人),所有询问中出现过的名字均在之前所描述的N对父子关系中出现过,且每个输入文件中第一个出现的名字所确定的人是其他所有人的公共祖先

输出

对于每组测试数据,对于每个小Hi的询问,按照在输入中出现的顺序,各输出一行,表示查询的结果:他们的所有共同祖先中辈分最低的一个人的名字。

样例输入

4
Adam Sam
Sam Joey
Sam Micheal
Adam Kevin
3
Sam Sam
Adam Sam
Micheal Kevin

样例输出

Sam
Adam
Adam

注意这道题是与之前的LCA问题有区别的,他只有一棵树,他的根节点就是第一个输入的那个名字,直接一个DFS(求深度)+LCA就可以求出;

这是一个已知根节点的LCA的模板题,比较水,就不多解释了;

ac:

#include<stdio.h>
#include<string.h>  
#include<math.h>  
  
#include<map>   
//#include<set>
#include<deque>  
#include<queue>  
#include<stack>  
#include<bitset> 
#include<string>  
#include<fstream>
#include<iostream>  
#include<algorithm>  
using namespace std;  

#define ll long long  
//#define max(a,b) (a)>(b)?(a):(b)
//#define min(a,b) (a)<(b)?(a):(b) 
#define clean(a,b) memset(a,b,sizeof(a))// 水印 
//std::ios::sync_with_stdio(false);
const int MAXN=1e5+10;
const int INF=0x3f3f3f3f;
const ll mod=1e9+7;

struct node{
	int v,w,nxt;
	node(int _v=0,int _nxt=0):
	v(_v),nxt(_nxt){}
}edge[MAXN<<1];
int head[MAXN],ecnt;
int fa[MAXN][30],deep[MAXN];
int k;
int n,m;

map<string,int> mp1;
map<int,string> mp2;

void intt()
{
	mp1.clear();
	mp2.clear();
	clean(head,-1);
	clean(deep,0);
	clean(fa,-1);
	ecnt=0;
	k=1;
}

void add(int u,int v)
{
	edge[ecnt]=node(v,head[u]);
	head[u]=ecnt++;
}

void dfs(int u)
{
	for(int i=head[u];i+1;i=edge[i].nxt)
	{
		int temp=edge[i].v;
		if(deep[temp]==0)
		{
			deep[temp]=deep[u]+1;
			fa[temp][0]=u;//父节点是u 
			int up=0,pre=u;//从父节点开始向上找 
			while(fa[pre][up]>=0)
			{
				fa[temp][up+1]=fa[pre][up];//子节点的第2^(up+1)父辈是pre的2^up父辈 
				pre=fa[pre][up++];
			}
			dfs(temp);
		}
	}
}

int lca(int a,int b)
{
	if(deep[a]<deep[b])
		swap(a,b);
	int lim=log(deep[a])+1;
	//使他们的深度相同 
	for(int i=lim;i>=0;--i)
	{
		if(deep[fa[a][i]]>=deep[b])
			a=fa[a][i];
	}
	if(a==b)
		return a;
	for(int i=lim;i>=0;--i)
	{
		if(fa[a][i]!=fa[b][i])
		{
			a=fa[a][i];
			b=fa[b][i];
		}
	}
	if(fa[a][0]==fa[b][0])
		return fa[a][0];
	else
		return -1;
}

int main()
{
	std::ios::sync_with_stdio(false);
	intt();
	cin>>n;
	string name1,name2;
	for(int i=1;i<=n;++i)
	{
		cin>>name1>>name2;
		if(mp1[name1]==0)//name1对应int 
		{
			mp1[name1]=k;
			mp2[k++]=name1;
		}
		if(mp1[name2]==0)//name2对应int 
		{
			mp1[name2]=k;
			mp2[k++]=name2;
		}
		add(mp1[name1],mp1[name2]);
//		add(mp1[name2],mp1[name1]);
	}
	deep[1]=1;
	dfs(1);
	cin>>m;
	for(int i=1;i<=m;++i)
	{
		cin>>name1>>name2;
		if(name1==name2)
			cout<<name1<<endl;
		else
		{
			int ans=lca(mp1[name1],mp1[name2]);
			if(ans==-1)
				cout<<ans<<endl;
			else
				cout<<mp2[ans]<<endl;
		}
	}
}

猜你喜欢

转载自blog.csdn.net/qq_40482358/article/details/82431930