定义
若一张连通图中去掉任意一条边后仍然连通,则它是一个边双。
求法
可以发现,边双中不包含桥,因此将图中的桥去掉之后,剩下的图中的所有连通块都是边双。
例题(CodeForces - 652E Pursuit For Artifacts)
题意
给出一张无向图,一些边上有一个神器,每条边只能经过一次,问,从s走到t,能否顺便拿到至少一个神器。
做法
可以发现,如果走到一个边双中,那么可以经过这个边双的任意一条边后,从这个边双的任意一个点中走出来,也就意味着如果有一个神器在这个边双中,走到这个边双中的任意一点都可以拿到神器。
因此,可以将边双缩成一个点,这样原图会变成一棵由桥和边双组成的树,考虑一下经过的边双和桥中有无神器即可。
代码
#include<bits/stdc++.h>
#define mid ((l+r)>>1)
#define N 300100
using namespace std;
int n,m,first[N],bb=1,dfn[N],low[N],tt,cnt,ss[N],fa[N],s,t;
bool bri[N],vis[N],ok[N];
struct Bn
{
int to,next,sq;
} bn[N<<1];
vector<int>son[N],hv[N];
inline void add(int u,int v,int w)
{
bb++;
bn[bb].to=v;
bn[bb].next=first[u];
bn[bb].sq=w;
first[u]=bb;
}
void dfs(int now,int last)
{
dfn[now]=low[now]=++tt;
int p,q;
for(p=first[now];p!=-1;p=bn[p].next)
{
if(bn[p].to==last || dfn[bn[p].to]>dfn[now]) continue;
if(dfn[bn[p].to])
{
low[now]=min(low[now],dfn[bn[p].to]);
}
else
{
dfs(bn[p].to,now);
low[now]=min(low[now],low[bn[p].to]);
if(low[bn[p].to]>dfn[now])
{
bri[p/2]=1;
}
}
}
}
void Dfs(int now,int last)
{
int p,q;
vis[now]=1;
fa[now]=cnt;
ss[now]=cnt;
for(p=first[now];p!=-1;p=bn[p].next)
{
if(bn[p].to==last || bri[p/2] || vis[bn[p].to]) continue;
Dfs(bn[p].to,now);
}
}
void fd(int now,int last,bool have)
{
int i,j;
have|=ok[now];
if(now==fa[t])
{
have?puts("YES"):puts("NO");
exit(0);
}
for(i=0;i<son[now].size();i++)
{
if(son[now][i]==last) continue;
fd(son[now][i],now,have|hv[now][i]);
}
}
int main()
{
memset(first,-1,sizeof(first));
int i,j,p,q,o;
cin>>n>>m;
for(i=1; i<=m; i++)
{
scanf("%d%d%d",&p,&q,&o);
add(p,q,o),add(q,p,o);
}
dfs(1,-1);
for(i=1;i<=n;i++)
{
if(vis[i]) continue;
cnt++;
Dfs(i,-1);
}
for(i=1;i<=m;i++)
{
if(!bri[i]) continue;
p=ss[bn[i*2].to],q=ss[bn[i*2+1].to];
son[p].push_back(q);
son[q].push_back(p);
hv[p].push_back(bn[i*2].sq);
hv[q].push_back(bn[i*2].sq);
}
for(i=1;i<=m;i++)
{
if(bri[i]) continue;
ok[ss[bn[i*2].to]]|=bn[i*2].sq;
}
cin>>s>>t;
fd(fa[s],-1,0);
puts("666");
}