题目链接 http://acm.hdu.edu.cn/showproblem.php?pid=6604
题意:给你一个有向图,有向图中没有出度的点位指挥中心,每次询问给你俩个点,问有多少个点满足它被摧毁后,给你的俩个点不能全部到达指挥中心。
题解:对于这个有向图,我们建图的时候,对于每条边,我们可以建个反向边,然后我们从指挥中心反向拓扑排序,按拓扑排序的顺序,建一个树,记录它们的深度。然后用最近公共祖先求解就行了。
#include<iostream>
#include<cmath>
#include<vector>
#include<queue>
#include<algorithm>
using namespace std;
int du[100005];
vector<int>g[100005];
vector<int>rg[100005];
int deep[100005],root[100005][18];
int tp[100005],l;
void init(int n)
{
for(int i=0;i<=n;i++)
{
g[i].clear();
rg[i].clear();
ng[i].clear();
du[i]=0;
deep[i]=0;
for(int j=log2(n);j>=0;j--)root[i][j]=0;
}
l=0;
}
void tuopu(int n)
{
queue<int>q;
for(int i=1;i<=n;i++)
{
if(du[i]==0)
q.push(i);
}
while(!q.empty())
{
int p=q.front();
q.pop();tp[l]=p;l++;
for(int j=0;j<rg[p].size();j++)
{
int v=rg[p][j];
du[v]--;
if(du[v]==0)
{
q.push(v);
}
}
}
}
int _lca(int x, int y,int n)
{
if(deep[x] < deep[y]) swap(x, y);
int tmp = deep[x] - deep[y];
for(int i=log2(1.*tmp); i>=0; i--)
{
if((1<<i) & tmp) x = root[x][i];
}
if(x == y) return x;
for(int i=log2(1.0*n); i>=0; i--)
{
if(root[x][i] != root[y][i])
{
x = root[x][i];
y = root[y][i];
}
}
return root[x][0];
}
int main()
{
int n,m,t,u,v;
scanf("%d",&t);
while(t--)
{
scanf("%d%d",&n,&m);
init(n);
for(int i=1;i<=m;i++)
{
scanf("%d%d",&u,&v);
g[u].push_back(v);
rg[v].push_back(u);
du[u]++;
}
tuopu(n);
for(int i=0;i<l;i++)
{
int x=tp[i];
if(g[x].size()==0)
{
deep[x]=1;
root[x][0]=0;
continue;
}
int y=g[x][0];
for(int j=1;j<g[x].size();j++)
{
int v=g[x][j];
y=_lca(y,v,n);
}
deep[x]=deep[y]+1;
root[x][0]=y;
for(int j=0;j<log2(1.0*n);j++)
{
root[x][j+1]=root[root[x][j]][j];
}
}
int q,a,b;
scanf("%d",&q);
while(q--)
{
scanf("%d %d",&a,&b);
printf("%d\n",deep[a]+deep[b]-deep[_lca(a,b,n)]);
}
}
}