2019 杭电多校第三场 HDU - 6604 Blow up the city 支配树

题目连接: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;
}