您的位置:首页 > 其它

hdu4547——CD操作 LCA在线算法

2014-08-18 17:03 323 查看
Problem Description
  在Windows下我们可以通过cmd运行DOS的部分功能,其中CD是一条很有意思的命令,通过CD操作,我们可以改变当前目录。

  这里我们简化一下问题,假设只有一个根目录,CD操作也只有两种方式:

  

  1. CD 当前目录名\...\目标目录名 (中间可以包含若干目录,保证目标目录通过绝对路径可达)

  2. CD .. (返回当前目录的上级目录)

  

  现在给出当前目录和一个目标目录,请问最少需要几次CD操作才能将当前目录变成目标目录?


Input
输入数据第一行包含一个整数T(T<=20),表示样例个数;

每个样例首先一行是两个整数N和M(1<=N,M<=100000),表示有N个目录和M个询问;

接下来N-1行每行两个目录名A B(目录名是只含有数字或字母,长度小于40的字符串),表示A的父目录是B。

最后M行每行两个目录名A B,表示询问将当前目录从A变成B最少要多少次CD操作。

数据保证合法,一定存在一个根目录,每个目录都能从根目录访问到。


Output
请输出每次询问的结果,每个查询的输出占一行。


Sample Input
2
3 1
B A
C A
B C

3 2
B A
C B
A C
C A




Sample Output
2
1
2




Source
2013金山西山居创意游戏程序挑战赛——初赛(1)



之前学过一个tarjan的离线算法,这次学了个在线的倍增算法,先来简单介绍下这个算法

我们设p[u][i]表示u向上走2^i步后到达的点,那么显然有

p[u][i]=p[p[u][i-1][i-1]成立,意思就是说,u向上走2^i步后到达的点等于y先走2^(i-1)步到达的点再走2^(i-1)步到达的点

每次传入a,b两个点,问a,b的LCA,我们的操作是,先把a,b调到同一高度,那么这个高度就需要对整张图进行一次深搜,并且把p数组计算好,待用

调到同一高度之后,如果两个点相同,那么这就是他们的LCA,否则,他们的LCA一定还在上面,就要继续向上走,具体看代码吧

#include<cstdio>
#include<cstring>
#include<cmath>
#include<map>
#include<iostream>
#include<algorithm>

using namespace std;

const int N=100010;
const int power=30;
int p
[30];
int deep
;
int in
;
struct node
{
	int to;
	int next;
}edge
;
int head
,tot;

void addedge(int from,int to)
{
	edge[tot].to=to;
	edge[tot].next=head[from];
	head[from]=tot++;
}

void dfs(int u,int fa)
{
	for(int i=head[u];i!=-1;i=edge[i].next)
	{
		int v=edge[i].to;
		if(v==fa)
			continue;
		deep[v]=deep[u]+1;
		dfs(v,u);
	}
}

int lca(int a,int b)
{
	if(deep[a]<deep[b])
	{
		a^=b;
		b^=a;
		a^=b;
	}
	int d=deep[a]-deep[b];
	for(int i=0;i<power;i++)
	{
		if(d&(1<<i))//这个操作很巧妙,利用2进制的思想
			a=p[a][i];
	}
	if(a==b)
		return a;
	for(int i=power-1;i>=0;i--)
	{
 		if(p[a][i]!=p[b][i])//越过他们的LCA之后,这些p值肯定都是一样的,反之,就一定还没到
		 {
			a=p[a][i];
			b=p[b][i];
		}
	}
	return p[a][0];//如果不明白为什么是这个的话,可以自己在纸上模拟下
	
}

char str1[55],str2[55];

int main()
{
	int n,m;
	int t;
	scanf("%d",&t);
	while(t--)
	{
		memset(in,0,sizeof(in));
	    scanf("%d%d",&n,&m);
	    memset(head,-1,sizeof(head));
	    memset(p,0,sizeof(p));
	    tot=0;
		int cnt=0,i;
		map<string,int>num;
		num.clear();
		for(int i=1;i<=n-1;i++)
		{
			scanf("%s%s",str1,str2);
			if(num[str1]==0)
				num[str1]=++cnt;
			if(num[str2]==0)
				num[str2]=++cnt;
			addedge(num[str2],num[str1]);
			in[num[str1]]++;
			p[num[str1]][0]=num[str2];
		}
		for(i=1;i<=cnt;i++)
			if(in[i]==0)
				break;
		deep[i]=0;
		dfs(i,i);
		for(int j=1;j<power;j++)
			for(int i=1;i<=cnt;i++)
				p[i][j]=p[p[i][j-1]][j-1];
		for(int i=1;i<=m;i++)
		{
			scanf("%s%s",str1,str2);
			int a=num[str1];
			int b=num[str2];
			int c=lca(a,b);
			int ans=deep[a]-deep[c];
			if(c!=b)
				ans++;
			printf("%d\n",ans);
		}
	}
	return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: