题目连接:https://vjudge.net/problem/HDU-6604c++
题意:有向无环图,q次询问,对于x,y,有多少点能让至少一个到达不了出度为0的点学习
题解:学习了支配树后,瞬间简单起来,仍是最简单的一种,有向无环,反向边拓扑,按照拓扑序,创建支配树,对于询问的(x, y),结果也就是deep[x] + deep[y] - deep[lca(x, y)]。spa
#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; }