[Submit] [Go Back] [Status] [Discuss]
Home Page Go Back To top
LCA最近公共祖先,给出两个结点问其最近公共祖先,可以转化为rmq求解。
我们用dfs深搜得到一个数列在记录下每次询问的深度和结点第一次出现的位置
然后就转为求区间最小值
如例子中
index 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
occur 8 1 13 1 14 1 8 4 10 2 10 11 10 16 12 16 3 16 10 4 6 7 6 15 6 4 8 5 9 5 8
depth 0 1 2 1 2 1 0 1 2 3 2 3 2 3 4 3 4 3 2 1 2 3 2 3 2 1 0 1 2 1 0
first 2 10 17 8 28 21 22 1 29 9 12 15 3 5 24 14
搜索得到序列之后假如我们想求16和 7的 LCA
那么我们找16和7在序列中的位置通过first 数组查找发现在14---22
即 16 12 16 3 16 10 4 6 7在上面图上找发现正好是以4为根的子树。而我们只要找到其中一个深度最小的编号就可以了、
这时候我们就用到了RMQ算法。
维护一个dp数组保存其区间深度最小的下标,查找的时候返回就可以了。
比如上面我们找到深度最小的为4点,返回其编号即可。
///求log2的数组模板漏打一句debug半天 蒟蒻都这样
ACcode:
#pragma warning(disable:4786)//使命名长度不受限制 #pragma comment(linker, "/STACK:102400000,102400000")//手工开栈 #include <map> #include <set> #include <queue> #include <cmath> #include <stack> #include <cctype> #include <cstdio> #include <cstring> #include <stdlib.h> #include <iostream> #include <algorithm> #define rd(x) scanf("%d",&x) #define rd2(x,y) scanf("%d%d",&x,&y) #define rds(x) scanf("%s",x) #define rdc(x) scanf("%c",&x) #define ll long long int #define maxn 25000 #define mod 1000000007 #define INF 0x3f3f3f3f //int 最大值 #define FOR(i,f_start,f_end) for(int i=f_start;i<=f_end;++i) #define MT(x,i) memset(x,i,sizeof(x)) #define PI acos(-1.0) #define E exp(1) using namespace std; struct Edge{ int v,next; }edge[maxn]; int loop,n,cnt,e; int head[maxn]; int first[maxn];///结点第一次出现的位置下标 int occur[maxn];///结点出现的顺序数组(有重复) int depth[maxn];///结点在树中的深度与occur对应 int dp[maxn][20];///rmq数组求区间最小值 int mm[maxn];///log2 数组 bool flag[maxn];///记入结点是否入度 /* 入度(indegree):在有向图中,一个顶点v的入度 是指与该边相关联的入边(即边的尾是v)的条数 */ void init(){ MT(head,-1); MT(flag,false); MT(first,0); e=cnt=0; } void dfs(int u,int deep){ occur[++cnt]=u;///进入该结点记录 depth[cnt]=deep; first[u]=first[u]?first[u]:cnt;///记录第一次访问 int i=head[u]; while(i+1){ dfs(edge[i].v,deep+1); occur[++cnt]=u; depth[cnt]=deep; i=edge[i].next; } } void RMQ_Init(int m){ mm[0]=-1; FOR(i,1,m){ dp[i][0]=i;///记录最小值的下标 mm[i]=((i&(i-1))==0)?mm[i-1]+1:mm[i-1]; ///log2数组 } FOR(j,1,19) FOR(i,1,m) if(i+(1<<j)-1<=m) dp[i][j]=depth[dp[i][j-1]]<depth[dp[i+(1<<(j-1))][j-1]]? dp[i][j-1]:dp[i+(1<<(j-1))][j-1]; } void fun(){ init(); rd(n); int u,v; FOR(i,1,n-1){ rd2(u,v); ///加边 edge[e].v=v; edge[e].next=head[u]; head[u]=e++; flag[v]=true; } FOR(i,1,n) if(!flag[i]){///从根开始遍历 dfs(i,0); break; } RMQ_Init(cnt); } void RMQ(int x,int y){ int k=mm[y-x+1]; k=depth[dp[x][k]]<depth[dp[y-(1<<k)+1][k]]? dp[x][k]:dp[y-(1<<k)+1][k]; printf("%d\n",occur[k]); } int main(){ rd(loop); while(loop--){ fun(); int x,y; rd2(x,y); ///区间的左右端点 x=first[x]; y=first[y]; if(x>y) x^=y^=x^=y; //cout<<x<<y<<'\12'; RMQ(x,y); // FOR(i,x,y)printf("%4d",occur[i]); //FOR(i,1,cnt)printf("%2d ",i); //FOR(i,1,cnt)printf("%4d ",occur[i]); // FOR(i,1,cnt)printf("%2d ",depth[i]); //FOR(i,1,cnt)printf("%2d ",first[i]); // printf("occur %d \t depth %d \t first %d\n",occur[i],depth[i],first[i]); } return 0; } /* 2 16 1 14 8 5 10 16 5 9 4 6 8 4 4 10 1 13 6 15 10 11 6 7 10 2 16 3 8 1 16 12 16 7 5 2 3 3 4 3 1 1 5 3 5 */
第二种做法:
由于树中的每个节点都有通往根的一条路径,因此任一结点对都存在共同的祖先。我们边输入边构造树的双亲表示和多重链表,并通过先跟遍历计算每个节点的层次。为了计算的方便,树的多重链表用vector类储存双亲和层次用数组。
若x与y不同则分析x和y的层次:若x处于较深层次,则取x的父指针为x;否则取y的父指针为y,……,依次类推直到x=y为止。
ACcode:
#pragma warning(disable:4786)//使命名长度不受限制 #pragma comment(linker, "/STACK:102400000,102400000")//手工开栈 #include <map> #include <set> #include <queue> #include <cmath> #include <stack> #include <cctype> #include <cstdio> #include <cstring> #include <stdlib.h> #include <iostream> #include <algorithm> #define rd(x) scanf("%d",&x) #define rd2(x,y) scanf("%d%d",&x,&y) #define rds(x) scanf("%s",x) #define rdc(x) scanf("%c",&x) #define ll long long int #define maxn 100005 #define mod 1000000007 #define INF 0x3f3f3f3f //int 最大值 #define FOR(i,f_start,f_end) for(int i=f_start;i<=f_end;++i) #define MT(x,i) memset(x,i,sizeof(x)) #define PI acos(-1.0) #define E exp(1) using namespace std; vector <int> a[maxn];///多重链表中结点i的链表为一个vector int f[maxn],r[maxn];///f为父亲指针 r为层次or深度 void DFS(int u,int dep){///从dep层的u结点出发通过先根遍历每个点的深度or层次 r[u]=dep; for(vector<int>::iterator it=a[u].begin();it!=a[u].end();++it) DFS(*it,dep+1); } int main(){ int loop,num,n,x,y; rd(loop); FOR(i,1,loop){ rd(n); FOR(i,0,n-1)a[i].clear();///清空 MT(f,255); FOR(i,0,n-2){ rd2(x,y); a[x-1].push_back(y-1); f[y-1]=x-1; } for(int i=0;;++i) if(f[i]<0){DFS(i,0);break;}///从根出发dfs建树 rd2(x,y); x--,y--; for(;x!=y;)///若未找到lca则反复计算深层结点的父结点 if(r[x]>r[y])x=f[x]; else y=f[y]; printf("%d\n",x+1); } return 0; } /* 2 16 1 14 8 5 10 16 5 9 4 6 8 4 4 10 1 13 6 15 10 11 6 7 10 2 16 3 8 1 16 12 16 7 5 2 3 3 4 3 1 1 5 3 5 */