AT_abc318_g 题解
因为是图上路径是否经过某个点的问题,所以考虑建出圆方树,然后根据圆方树的性质,\(a\) 到 \(c\) 存在经过 \(b\) 的路径等价于 \(a,c\) 在圆方树上的路径经过 \(b\) 或者 \(b\) 所连接的方点,考虑暴力在圆方树上跳 LCA 即可,时间复杂度 \(O(n + m)\)。
#include<bits/stdc++.h>
using namespace std;
const int maxn = 4e5+114;
int n,m,cnt;
std::vector<int> G[maxn],T[maxn<<1];
int fa[maxn];
int dfn[maxn], low[maxn], dfc;
int stk[maxn],tp;
int f[maxn];
int dep[maxn<<1];
int val[maxn<<1];
int a,b,c;
void Tarjan(int u) {
low[u] = dfn[u] = ++dfc;
stk[++tp] = u;
for (int v : G[u]){
if (!dfn[v]) {
Tarjan(v);
low[u] = std::min(low[u], low[v]);
if (low[v] == dfn[u]) {
++cnt;
for (int x = 0; x != v; --tp) {
x = stk[tp];
T[cnt].push_back(x);
T[x].push_back(cnt);
val[cnt]|=val[x];
}
val[cnt]|=val[u];
T[cnt].push_back(u);
T[u].push_back(cnt);
}
}else
low[u] = std::min(low[u], dfn[v]);
}
}
void build(){
cnt=n;
for(int u=1;u<=n;u++){
if(!dfn[u]) Tarjan(u), --tp;
}
for(int i=n+1;i<=cnt;i++){
for(int u:T[i]){
f[u]=i;
}
}
}
void dfs(int u,int father){
fa[u]=father;
dep[u]=dep[father]+1;
for(int v:T[u]){
if(v==father) continue;
dfs(v,u);
}
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin>>n>>m;
int a,b,c;
cin>>a>>b>>c;
val[b]=1;
for(int i=1;i<=m;i++){
int u,v;
cin>>u>>v;
G[u].push_back(v);
G[v].push_back(u);
}
build();
dfs(1,0);
int ans=0;
ans|=(val[a]|val[c]);
while(a!=c){
if(dep[a]<dep[c]) swap(a,c);
a=fa[a];
ans|=(val[a]|val[c]);
}
cout<<(ans==1?"Yes\n":"No\n");
return 0;
}
其实这题还可以多组询问,考虑把询问挂在 \(b\) 上然后树剖即可。时间复杂度是 \(O(q \log n + n \log n + m)\) 的。