支配树:树上的任何一个点的所有祖先都是在有向无环图中这个点到树根的所有路径的必经点。
有向无环图支配树求法:
由于在有向无环图中可能有很多联通块,所有需要先记录入度,然后从每个入度为0的点开始跑bfs,每个点在支配树上的父亲就是在有向无环图中它的所有父亲的LCA。
#include<iostream>
#include<cstdio>
#include<vector>
#include<queue>
#include<cmath>
#include<cstring>
using namespace std;
const int MAX_N=100100;
vector<int>v[MAX_N];
int head[MAX_N],ver[2*MAX_N],Next[2*MAX_N];
int sum[MAX_N];
int tot,tt;
int deep[MAX_N];
int dp[MAX_N][20];
void Add(int x,int y){
ver[++tot]=y;Next[tot]=head[x];head[x]=tot;
}
queue<int>q;
int LCA(int a,int b){
int k=tt,i;
if(deep[a]<deep[b])
swap(a,b);
while(deep[a]!=deep[b]){
for(i=k;i>=0;i--){
if(deep[dp[a][i]]>=deep[b])
a=dp[a][i];
}
}
if(a==b){
return b;
}
for(i=k;i>=0;i--){
if(dp[a][i]!=dp[b][i]){
a=dp[a][i];
b=dp[b][i];
}
}
return dp[a][0];
}
void solve(int x){
int lca=v[x][0];
for(int i=1;i<v[x].size();i++){
int y=v[x][i];
lca=LCA(lca,y);
}
dp[x][0]=lca;
deep[x]=deep[lca]+1;
for(int i=1;i<=tt;i++)
dp[x][i]=dp[dp[x][i-1]][i-1];
}
void topo(){
int i;
while(!q.empty()){
int x=q.front();
q.pop();
for(i=head[x];i;i=Next[i]){
int y=ver[i];
sum[y]--;
if(sum[y]==0){
q.push(y);
solve(y);
}
}
}
}
int main(void){
int n,m,i,x,y,qq;
int T;
cin>>T;
while(T--){
scanf("%d%d",&n,&m);
tt=(int)(log(n+1)/log(2))+1;
tot=0;
for(i=1;i<=n+1;i++){
head[i]=0;
v[i].clear();
sum[i]=0;
}
for(i=1;i<=m;i++){
scanf("%d%d",&x,&y);
Add(y,x);
sum[x]++;
v[x].push_back(y);
}
for(i=1;i<=n;i++){
if(!sum[i]){
Add(n+1,i);
sum[i]++;
v[i].push_back(n+1);
}
}
deep[n+1]=1;
q.push(n+1);
topo();
scanf("%d",&qq);
for(i=0;i<qq;i++){
scanf("%d%d",&x,&y);
int lca=LCA(x,y);
printf("%d\n",deep[x]+deep[y]-deep[lca]-1);
}
}
return 0;
}