uoj675
加強:\(\sum k\le6\times10^5\)
暴力
\(u\) 在 \(s\Rightarrow t\) 路徑上 \(\iff\) 正圖上 \(s\Rightarrow u\) 且反圖上 \(u\Rightarrow t\)
時間複雜度 \(O((n+m)q)\)
正解
只關心可達性,不妨 SCC 縮點成 DAG。注意到一個奇怪的條件:
對於三座城市 \(x,y,z\),若 \(x\Rightarrow z\) 且 \(y\Rightarrow z\),那麼有 \(x\Rightarrow y\) 或 \(y\Rightarrow x\)。
若存在邊 \((x,z),(x,y),(y,z)\),可以刪去邊 \((x,z)\)。這樣每個點的前驅唯一,又有原圖弱連通,即為葉向樹
實現上有兩種做法:拓撲排序,保留每個點的最後一條入邊;從零入度點開始 dfs,優先遞迴編號大的兒子,保留 dfs 樹(利用 tarjan 縮點的性質)
葉向樹
\(k=0\) \(t\) 在 \(s\) 子樹內時答案為深度差
\(k=1\) 只需要討論走不走新邊,轉化為子樹和根鏈求交
\(k=2\)
時間複雜度 \(O(n\log n+\sum k\log k)\)
#ifndef FT
#define NDEBUG
#endif
#include <bits/stdc++.h>
#include <bits/extc++.h>
using namespace std; using namespace __gnu_pbds; using namespace __gnu_cxx;
#define For(i,x,y,...) for(int i=x,##__VA_ARGS__;i<=(y);++i)
#define rFor(i,x,y,...) for(int i=x,##__VA_ARGS__;i>=(y);--i)
#define Rep(i,x,y,...) for(int i=x,##__VA_ARGS__;i<(y);++i)
#define pb emplace_back
#define sz(a) int((a).size())
#define all(a) (a).begin(),(a).end()
#define fi first
#define se second
#define mkp make_pair
typedef long long LL; typedef vector<int> Vi; typedef pair<int,int> Pii;
auto ckmax=[](auto &x,auto y) { return x<y ? x=y,true : false; };
auto ckmin=[](auto &x,auto y) { return y<x ? x=y,true : false; };
sfmt19937 mt(chrono::steady_clock::now().time_since_epoch().count());
int rnd(int l,int r) { return uniform_int_distribution<>(l,r)(mt); }
template<typename T=int>T read() { T x; cin>>x; return x; }
const int mod = 998244353;
struct mint {
int x; mint(int x=0):x(x<0?x+mod:x<mod?x:x-mod){}
mint(LL y) { y%=mod, x=y<0?y+mod:y; }
mint& operator += (const mint &y) { x=x+y.x<mod?x+y.x:x+y.x-mod; return *this; }
mint& operator -= (const mint &y) { x=x<y.x?x-y.x+mod:x-y.x; return *this; }
mint& operator *= (const mint &y) { x=1ll*x*y.x%mod; return *this; }
friend mint operator + (mint x,const mint &y) { return x+=y; }
friend mint operator - (mint x,const mint &y) { return x-=y; }
friend mint operator * (mint x,const mint &y) { return x*=y; }
}; mint Pow(mint x,LL y=mod-2) { mint z(1);for(;y;y>>=1,x*=x)if(y&1)z*=x;return z; }
const int N = 3e5+5;
namespace T { // 葉向樹
int n,rt,ind,val[N],sum[N],dfn[N],st[19][N];
Vi e[N];
int _min(int x,int y) { return dfn[x]<dfn[y] ? x : y; }
void dfs(int u,int fa) {
dfn[u] = ++ind, st[0][ind] = fa, sum[u] = sum[fa] + val[u];
for(int v : e[u]) dfs(v,u);
}
int lca(int u,int v) {
if( u == v ) return u;
if( (u=dfn[u]) > (v=dfn[v]) ) swap(u,v);
int k = __lg(v-u++);
return _min(st[k][u], st[k][v-(1<<k)+1]);
}
void main() {
// cerr<<"T:\n"; For(i,1,n) for(int j : e[i]) cerr<<" "<<i<<" "<<j<<'\n';
dfs(rt,0);
For(i,1,18) For(j,1,n-(1<<i)+1) st[i][j] = _min(st[i-1][j], st[i-1][j+(1<<i-1)]);
}
}
namespace DAG {
int n,deg[N];
Vi e[N];
void main() {
// cerr<<"DAG:\n"; For(i,1,n) for(int j : e[i]) cerr<<" "<<i<<" "<<j<<'\n';
T::n = n;
For(i,1,n) for(int j : e[i]) ++deg[j];
queue<int> q;
For(i,1,n) if( !deg[i] ) q.emplace(i), T::rt = i;
assert(sz(q)==1);
while( sz(q) ) {
int u = q.front(); q.pop();
for(int v : e[u]) if( !--deg[v] ) q.emplace(v), T::e[u].pb(v);
}
}
}
namespace G { // 原圖
int n,m,ind,dfn[N],low[N],id[N];
Vi e[N];
void tj(int u) {
static int t,s[N];
dfn[u] = low[u] = ++ind, id[u] = -1, s[++t] = u;
for(int v : e[u])
if( !dfn[v] ) tj(v), ckmin(low[u], low[v]);
else if( !~id[v] ) ckmin(low[u], dfn[v]);
if( dfn[u] == low[u] ) {
++DAG::n;
do id[s[t]] = DAG::n, ++T::val[DAG::n]; while( u != s[t--] );
}
}
void main() {
For(i,1,n) if( !dfn[i] ) tj(i);
For(i,1,n) for(int j : e[i]) if( id[i] != id[j] ) DAG::e[id[i]].pb(id[j]);
// cerr<<"id: "; For(i,1,n) cerr<<id[i]<<" "; cerr<<'\n';
}
}
int mm;
Vi ver;
struct VT {
int head[N];
bool vis[N];
struct { int nxt,to; } e[30];
void clr() { for(int i : ver) head[i] = vis[i] = 0; }
void dfs(int u) {
if( vis[u] ) return; vis[u] = 1;
for(int i = head[u], v; v = e[i].to, i; i = e[i].nxt) dfs(v);
}
} g1,g2;
void adde(int x,int y) {
g1.e[++mm] = {g1.head[x],y}, g1.head[x] = mm;
g2.e[mm] = {g2.head[y],x}, g2.head[y] = mm;
// cerr<<" "<<x<<" "<<y<<" "<<'\n';
}
void MAIN() {
int q,k; cin>>G::n>>G::m>>q>>k; For(i,1,G::m, x,y) cin>>x>>y, G::e[x].pb(y);
G::main(), DAG::main(), T::main();
while( q-- ) {
// cerr<<"VT:\n";
int s,t; s = G::id[read()], t = G::id[read()], ver = {s,t};
For(i,1,k, x,y)
x = G::id[read()], y = G::id[read()],
ver.pb(x), ver.pb(y), adde(x,y);
auto cmp = [](int x,int y){return T::dfn[x]<T::dfn[y];};
sort(all(ver),cmp), ver.erase(unique(all(ver)),ver.end());
rFor(i,sz(ver)-1,1) ver.pb(T::lca(ver[i-1],ver[i]));
ver.pb(T::rt), sort(all(ver),cmp), ver.erase(unique(all(ver)),ver.end());
Rep(i,1,sz(ver)) adde(T::lca(ver[i-1],ver[i]),ver[i]);
g1.dfs(s), g2.dfs(t);
int ans = 0;
for(int i : ver) if( g1.vis[i] && g2.vis[i] ) ans += T::val[i];
For(i,k+1,mm) if(int x = g2.e[i].to, y = g1.e[i].to; g1.vis[x] && g2.vis[y] )
ans += T::sum[T::st[0][T::dfn[y]]] - T::sum[x];
cout<<ans<<'\n';
g1.clr(), g2.clr(), mm = 0;
}
} signed main() {
#ifdef FT
freopen("in","r",stdin); freopen("out","w",stdout);
#endif
ios::sync_with_stdio(0);cin.tie(0);
int lft=1; while( lft-- ) {
MAIN();
}
return 0;
}