题目链接
题意:给定n个点m条边的无向图,然后q组数对(x,y),问是否存在一种方案使m条边定向后,同时满足可以从x到y。能输出Yes,否则输出No。
n , m , q < = 2 e 5 n,m,q<=2e5 n,m,q<=2e5
感觉是将无向图定向后,问是否构成强连通图题目的升级版。
因为是无向图可以求出这个图的边双连通分量,对于在双连通分量中的 ( x , y ) (x,y) (x,y)总能到达。接着不在一个双连通分量中,如果该双连通分量不连通,那么直接输出 N o No No,因为这两个点不连通就无法到达。可以知道互连的双连通分量构成一颗树,就转化为求 l c a lca lca问题。对于 x x x到 y y y的路径,其从 x x x到 l c a ( x , y ) lca(x,y) lca(x,y)这条路一定是向上走,从 l c a ( x , y ) lca(x,y) lca(x,y)到 y y y这条路一定向下走,所以可以用树上差分维护,维护两个差分数组 d [ ] , d 1 [ ] d[],d1[] d[],d1[],表示某个点到其父结点的这条边上的方向是否向上/下走。如果同时存在 d [ i ] d[i] d[i]和 d 1 [ i ] d1[i] d1[i],那么就是不行的,因为一条边只能有一个方向。
题目可能会出现重边,出现重边很好判断,就是这个边一定不是桥,即边的两个端点一定是在一个双连通分量中。
时间复杂度: O(
n
l
o
g
n
nlogn
nlogn)
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=2e5+10;
vector<int>G[maxn],dcc[maxn];
vector<pair<int,int>>G1[maxn],ins;
void add1(int x,int y)
{
G1[x].push_back({y,G1[y].size()});
G1[y].push_back({x,G1[x].size()-1});
}
void add(int x,int y)
{
G[x].push_back(y); G[y].push_back(x);
}
int dfn[maxn],low[maxn],k,st[maxn];
int top,id[maxn],cnt;
void tarjan(int u,int fa)
{
low[u]=dfn[u]=++k;
st[++top]=u;
for(int i=0;i<G1[u].size();i++)
{
if(i==fa) continue;
auto v1=G1[u][i];
int v=v1.first;
if(!dfn[v]) {
tarjan(v,v1.second); //传to的方向边 防止有重边
low[u]=min(low[u],low[v]);
if(low[v]>dfn[u]) ins.push_back({u,v});
}
else low[u]=min(low[u],dfn[v]);
}
if(low[u]==dfn[u])
{
dcc[++cnt].push_back(u);
id[u]=cnt;
while(st[top]!=u)
{
dcc[cnt].push_back(st[top]);
id[st[top]]=cnt;top--;
}
top--;
}
}
int par[maxn];
const int lg=20;
int fa[maxn][lg+5]; //fa[i][j]: 从i向上跳2^j步后的点
int dep[maxn];
void dfs(int u,int fa1,int id)
{
par[u]=id;
dep[u]=dep[fa1]+1;
for(int i=0;i<G[u].size();i++)
{
int v=G[u][i];
if(v==fa1) continue;
fa[v][0]=u;
for(int j=1;j<=lg;j++)
fa[v][j]=fa[fa[v][j-1]][j-1];
dfs(v,u,id);
}
}
int lca(int x,int y)
{
if(dep[x]<dep[y]) swap(x,y);
for(int i=lg;i>=0;i--)
if(dep[fa[x][i]]>=dep[y])
x=fa[x][i];
if(x==y) return x; //结束循环后深度肯定一样 此时相等表明y是初始x的祖先
for(int i=lg;i>=0;i--)
if(fa[x][i]!=fa[y][i])
{
x=fa[x][i];y=fa[y][i];
}
return fa[x][0]; //此时x与y是不等的 但是他们的父亲节点是一样的
}
int d[maxn],d1[maxn],vis[maxn];
bool fl=false;
int n,m,q;
void dfs1(int u,int fa)
{
vis[u]=1;
for(auto v:G[u])
{
if(v==fa) continue;
dfs1(v,u);
d[u]+=d[v];d1[u]+=d1[v];
}
if(d[u]&&d1[u]) {
fl=true;
}
}
int main()
{
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
cin>>n>>m>>q;
for(int i=1;i<=m;i++)
{
int x,y;cin>>x>>y;
add1(x,y);
}
for(int i=1;i<=n;i++)
if(!dfn[i]) tarjan(i,-1);
for(auto i:ins)
add(id[i.first],id[i.second]);
for(int i=1;i<=cnt;i++)
if(!dep[i]) dfs(i,i,i); //求每颗树的lca
while(q--)
{
int x,y;cin>>x>>y;
if(id[x]==id[y]) continue;
x=id[x];y=id[y];
if(par[x]!=par[y]) {
fl=true;continue;
}
d[x]++;d[lca(x,y)]--;
d1[y]++;d1[lca(x,y)]--;
}
for(int i=1;i<=cnt;i++)
if(!vis[i]) dfs1(i,i);
if(fl) cout<<"No\n";
else cout<<"Yes\n";;
return 0;
}