题目链接:点我啊╭(╯^╰)╮
题目大意:
个城市组成有向无环图,出度为
的城市为指挥城市
次查询,每次给出两座重要城市
问炸毁任意一个城市,使得从指挥城市不能到达两座城市中的任意一座的方案数
解题思路:
先反向建图,要炸毁的城市就是从若干起点到两座城市的必经点
则直接建
支配树
若干起点到一座城市的必经点就是这个点的深度
两个城市的答案考虑容斥
即两座城市的深度减去
的深度,注意源点的深度
核心:DAG支配树的特性
#include<bits/stdc++.h>
#define rint register int
#define deb(x) cerr<<#x<<" = "<<(x)<<'\n';
using namespace std;
typedef long long ll;
typedef pair <int,int> pii;
const ll mod = 1e9 + 7;
const int maxn = 1e5 + 10;
int T, n, m, q, cnt;
int tp[maxn], in[maxn];
int dep[maxn], f[maxn][25];
vector <int> t[maxn];
vector <int> v[maxn], fa[maxn];
int lca(int x, int y){
if(dep[x]<dep[y]) swap(x, y);
for(int i=20; ~i; i--)
if(dep[f[x][i]]>=dep[y])
x = f[x][i];
if(x == y) return x;
for(int i=20; ~i; i--)
if(f[x][i] ^ f[y][i])
x=f[x][i], y=f[y][i];
return f[x][0];
}
void build(int x){
int lcaf = fa[x][0];
for(int i=1; i<fa[x].size(); i++)
lcaf = lca(lcaf, fa[x][i]);
t[lcaf].push_back(x);
dep[x] = dep[lcaf] + 1;
f[x][0] = lcaf;
for(int i=1; i<=20; i++)
f[x][i] = f[f[x][i-1]][i-1];
}
void tp_sort(){
queue <int> Q;
for(int i=1; i<=n; i++)
if(!in[i]){
in[i]++;
v[0].push_back(i);
fa[i].push_back(0);
}
Q.push(0);
while(!Q.empty()){
int q = Q.front();
Q.pop();
tp[cnt++] = q;
for(auto i : v[q]){
in[i]--;
if(!in[i]){
Q.push(i);
build(i);
}
}
}
}
void init(){
cnt = 0;
for(int i=0; i<maxn; i++){
in[i] = dep[i] = 0;
v[i].clear();
fa[i].clear();
t[i].clear();
}
}
int main() {
scanf("%d", &T);
while(T--){
init();
scanf("%d%d", &n, &m);
for(int i=0, x, y; i<m; i++){
scanf("%d%d", &y, &x);
v[x].push_back(y);
fa[y].push_back(x);
in[y]++;
}
tp_sort();
scanf("%d", &q);
while(q--){
int x, y, ans = 0;
scanf("%d%d", &x, &y);
int lcaf = lca(x, y);
ans = dep[x] + dep[y] - dep[lcaf];
printf("%d\n", ans);
}
}
}