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
Sample Output
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一定还在上面,就要继续向上走,具体看代码吧
在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; }
相关文章推荐
- HDU - 4547 - CD操作(LCA)
- HDU 4547 CD操作 LCA
- HDU 4547 CD操作(LCA)
- hdu 4547 CD操作
- hdu_4547_CD操作(在线LCA)
- hdu_4547_CD操作(在线LCA)
- HDU 4547 CD操作 LCA
- hdu 4547 CD操作
- CD操作 HDU - 4547 (LCA-离线tarjan)
- HDU_4547_CD操作(LCA+tarjan)
- Hdu 4547 CD操作 LCA问题
- Hdu 4547 CD操作 LCA问题
- HDU 4547 - CD操作 (LCA)
- hdu 4547 CD操作(金山居 LCA算法)
- 【HDU 4547 CD操作】LCA问题 Tarjan算法
- HDU 4547 CD操作(LCA倍增)
- HDU 4547 CD操作 2013金山西山居创意游戏程序挑战赛——初赛(1)
- hdu 4547 CD操作 LCA倍增算法
- HDU-4547 CD操作 LCA
- HDU 4547 CD操作,lca 求两节点的公共祖先和深度