题目链接:https://vjudge.net/problem/HDU-6604
题意:有向无环图,q次询问,对于x,y,有多少点能让至少一个到达不了出度为0的点
题解:学习了支配树后,瞬间简单起来,还是最简单的一种,有向无环,反向边拓扑,按照拓扑序,建立支配树,对于询问的(x, y),结果也就是deep[x] + deep[y] - deep[lca(x, y)]。
#include <bits/stdc++.h>
using namespace std;
#define pb push_back
const int N = 100100;
vector<int> v[N], vf[N], vz[N];
int n, m, in[N];
int a[N], q, len;
int deep[N], dp[N][22];
int ans[N];
void topo() {
int now, to;
queue<int> q;
for(int i = 1; i <= n; i++) {
if(in[i] == 0)
q.push(i);
}
while(!q.empty()) {
a[++len] = q.front(); q.pop();
now = a[len];
for(int i = 0; i < vf[now].size(); i++) {
to = vf[now][i];
in[to]--;
if(in[to] == 0)
q.push(to);
}
}
}
int getlca(int x, int y) {
if(deep[x] < deep[y]) swap(x, y);
int cnt = deep[x] - deep[y];
for(int i = 0; i < 20; i++)
if((1 << i) & cnt)
x = dp[x][i];
if(x == y) return x;
for(int i = 19; i >= 0; i--){
if(dp[x][i] != dp[y][i]) {
x = dp[x][i];
y = dp[y][i];
}
}
return dp[x][0];
}
int main() {
int T;
int x, y;
scanf("%d", &T);
while(T--) {
scanf("%d %d", &n, &m);
for(int i = 0; i <= n; i++) {
v[i].clear();
vf[i].clear();
vz[i].clear();
in[i] = 0;
for(int j = 0; j < 20; j++)
dp[i][j] = 0;
}
len = 0;
for(int i = 1; i <= m; i++) {
scanf("%d %d", &x, &y);
v[x].pb(y);
vf[y].pb(x);
in[x]++;
}
topo();
for(int i = 1; i <= n; i++) {
x = a[i];
if(v[x].size() == 0) {
v[0].pb(x);
deep[x] = 1;
continue;
}
y = v[x][0];
for(int i = 1; i < v[x].size(); i++) {
y = getlca(y, v[x][i]);
}
vz[y].pb(x);
deep[x] = deep[y] + 1;
dp[x][0] = y;
for(int i = 1; i < 20; i++)
dp[x][i] = dp[dp[x][i - 1]][i - 1];
}
scanf("%d", &q);
while(q--) {
scanf("%d %d", &x, &y);
printf("%d\n", deep[x] + deep[y] - deep[getlca(x, y)]);
}
}
return 0;
}