注意到 \(1\) 到一個 \(b\) 子樹內的點 \(x\) 的路徑可以拆成 \(1\to p\to q\to x\) 的形式,其中 \(1\to p\) 走樹邊,\(p\to q\) 為在點 \(p\) 從樹邊走出去,在點 \(q\) 走回來,然後 \(q\) 再走樹邊走到 \(x\)。
考慮 \(f_i\) 為最小的 \(d\),滿足斷掉 \(i\) 深度為 \(d\) 的祖先到 \(i\) 的鏈後,\(1\) 仍然能到 \(i\)。那麼一個點 \(x\) 能到達當且僅當存在 \(q\) 在 \(b\to x\) 路徑上,且 \(f_q\le dep_a\),即 \(\min\limits_{q\in path(b,x)}f_q\le dep_a\)。
考慮怎麼求 \(f\)。這個可以按照拓撲序從小往大列舉 \(u\),同時處理所有非樹邊 \(u\to v\),貢獻為 \(u\) 到 \(\operatorname{LCA}(u, v)\) 這條鏈上 \(f\) 的最大值。因為此時 \(u\to \operatorname{LCA}(u, v)\) 鏈上的 \(f\) 都已知,可以直接倍增。
現在我們已知了 \(f\),把每組詢問掛在 \(b\) 上,動態維護 \(\min f\) 的權值線段樹,不難線段樹合併做到 \(O((n+m+q)\log n)\)。
code
#include <bits/stdc++.h>
#define ALL(x) begin(x), end(x)
using namespace std;
void file() {
freopen("1.in", "r", stdin);
freopen("1.out", "w", stdout);
}
using ll = long long;
using PII = pair<int, int> ;
const int kInf = 1e9;
const int kN = 1.5e5 + 5;
int n, m, q;
array<int, kN> fa, ord, in, dep, f, ans;
array<bool, kN> intr, vis;
array<PII, kN> ed;
array<vector<PII>, kN> g, qry;
array<vector<int>, kN> tr, buc;
array<array<int, kN>, 20> jp, mn;
void toposort() {
for(int i = 1; i <= m; i++)
in[ed[i].second]++;
queue<int> q;
for(int i = 1; i <= n; i++)
if(!in[i]) q.push(i);
int tot = 0;
while(q.size()) {
int x = q.front(); q.pop();
ord[++tot] = x;
for(auto k : g[x]) {
int to = k.first;
if(!--in[to]) q.push(to);
}
}
}
void build() {
for(int i = 1; i <= n; i++)
sort(ALL(g[i]), greater<> ());
dep[0] = -1;
for(int i = 1, x = 1; i <= n; i++) {
vis[x] = 1;
for(bool flag = 0; x; x = fa[x]) {
while(g[x].size()) {
auto k = g[x].back(); g[x].pop_back();
int to = k.first;
if(vis[to]) continue;
else {
intr[k.second] = flag = 1;
f[to] = dep[to] = dep[x] + 1, fa[to] = x;
tr[x].push_back(to), x = to;
break;
}
}
if(flag) break;
}
}
}
void init(int x) {
mn[0][x] = f[x];
for(int i = 1; i < 19; i++)
mn[i][x] = min(mn[i - 1][x], mn[i - 1][jp[i - 1][x]]);
}
int lca(int x, int y) {
if(dep[x] < dep[y]) swap(x, y);
for(int i = 19; ~i; i--)
if(dep[jp[i][x]] >= dep[y]) x = jp[i][x];
if(x == y) return x;
for(int i = 19; ~i; i--)
if(jp[i][x] ^ jp[i][y])
x = jp[i][x], y = jp[i][y];
return fa[x];
}
int query(int anc, int x) {
int ans = f[anc];
for(int i = 19; ~i; i--)
if(dep[jp[i][x]] >= dep[anc])
ans = min(ans, mn[i][x]), x = jp[i][x];
return ans;
}
const int kS = 5e6 + 5;
struct SGT {
int tot = 0;
array<int, kN> root;
array<int, kS> ls, rs, sum;
void pu(int o) { sum[o] = sum[ls[o]] + sum[rs[o]]; }
void modify(int& o, int l, int r, int x, int v) {
if(!o) o = ++tot;
sum[o] += v;
if(l == r) return ;
int mid = (l + r) >> 1;
if(mid < x) modify(rs[o], mid + 1, r, x, v);
else modify(ls[o], l, mid, x, v);
}
void erase(int& o, int l, int r, int x, int y) {
if((l > y) || (r < x)) return ;
if((l >= x) && (r <= y)) return void(o = 0);
int mid = (l + r) >> 1;
erase(ls[o], l, mid, x, y);
erase(rs[o], mid + 1, r, x, y);
pu(o);
}
int query(int o, int l, int r, int x, int y) {
if((l > y) || (r < x)) return 0;
if((l >= x) && (r <= y)) return sum[o];
int mid = (l + r) >> 1;
return query(ls[o], l, mid, x, y) + query(rs[o], mid + 1, r, x, y);
}
int merge(int x, int y, int l, int r) {
if(!x || !y) return x + y;
sum[x] += sum[y];
if(l == r) return x;
int mid = (l + r) >> 1;
ls[x] = merge(ls[x], ls[y], l, mid);
rs[x] = merge(rs[x], rs[y], mid + 1, r);
return x;
}
}sgt;
void dfs(int x) {
for(int to : tr[x])
dfs(to), sgt.root[x] = sgt.merge(sgt.root[x], sgt.root[to], 0, n);
int sum = sgt.query(sgt.root[x], 0, n, f[x] + 1, n) + 1;
sgt.erase(sgt.root[x], 0, n, f[x] + 1, n);
sgt.modify(sgt.root[x], 0, n, f[x], sum);
for(auto k : qry[x]) {
int dep = k.first, id = k.second;
ans[id] = sgt.query(sgt.root[x], 0, n, dep + 1, n);
}
}
int main() {
// file();
ios::sync_with_stdio(0), cin.tie(0);
cin >> n >> m >> q;
for(int i = 1, u, v; i <= m; i++) {
cin >> u >> v;
ed[i] = PII {u, v};
g[u].emplace_back(v, i);
}
toposort(), build();
for(int i = 1; i <= m; i++) {
int u = ed[i].first, v = ed[i].second;
if(!intr[i]) buc[u].push_back(v);
}
jp[0] = fa;
for(int i = 1; i < 20; i++)
for(int j = 1; j <= n; j++)
jp[i][j] = jp[i - 1][jp[i - 1][j]];
for(auto& k : mn) k.fill(kInf);
for(int i = 1; i <= n; i++) {
int x = ord[i]; init(x);
for(int to : buc[x]) {
int anc = lca(x, to);
f[to] = min(f[to], query(anc, x));
}
}
for(int i = 1, a, b; i <= q; i++) {
cin >> a >> b;
qry[b].emplace_back(dep[a], i);
}
dfs(1);
for(int i = 1; i <= q; i++)
cout << ans[i] << "\n";
return 0;
}