快来踩爆这个蒟蒻吧|

Little_corn

园龄:1年1个月粉丝:10关注:17

2024-04-25 13:05阅读: 14评论: 0推荐: 0

割点&&桥&&边双&&点双

定义:

割点:将原图中的某一点以及它所连的边删除后,原图不连通

:将原图中的某一边删除后,原图不连通。

边双连通分量:原图中意删除一边后还连通的极大连通子图。

点双连通分量:原图中任意删除一点后还连通的极大连通子图。

求法:

割点:

考虑原图的 dfs 生成树,对于树边更新 : low[u]=min(low[u],low[v]),对于非树边 low[u]=min(low[u],dfn[v])

关键点:假如对于 u 的子节点 vlow[v]dfn[u],那么 u 为割点

为什么呢?原因就在于 low 的定义是能够到达的节点最早的时间戳,那么假如 low[u]dfn[v] ,就代表 u 实际上并不能通过别的节点来到 u 以上的节点,就代表假如割掉了 uv 就不能到达 u 以上的节点了,所以割掉了 uv 就寄了。

代码:

void tarjan(int u,int fa){
dfn[u]=low[u]=++tim;
int child=0;
for(int i=head[u];i;i=edges[i].next){
int v=edges[i].v;
if(!dfn[v]){
child++;
tarjan(v,u);
low[u]=min(low[u],low[v]);
if(fa!=-1&&low[v]>=dfn[u]){
cut[u]=true;
}
}
else if(dfn[v]<dfn[u]&&v!=fa){
low[u]=min(low[u],dfn[v]);
}
}
if(fa==-1&&child>=2){
cut[u]=true;//这里注意 u 为根节点的情况
}
return;
}

桥:

还是一样,使用 tarjan 求解。

这里有一个性质:low[v]>dfn[u]uv 连的边就是一个桥了。

证明同上。

代码:

#include<bits/stdc++.h>
#define L long long
using namespace std;
const int mod=1000000007,N=1145;
L pre[N],dfn[N],low[N],n,m,dt,ans;
bool vis[N];
vector<L> G[N];
struct edge{
L from,to;
}a[N];
inline int read()
{
int x=0,f=1;char ch=getchar();
while (ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();}
while (ch>='0'&&ch<='9'){x=x*10+ch-48;ch=getchar();}
return x*f;
}
void dfs(L fa ,L now){
dfn[now]=low[now]=++dt;
L len=G[now].size();
for(L i=0;i<len;i++){
L next=G[now][i];
if(next!=fa&&dfn[next]) low[now]=min(low[now],dfn[next]);
if(!dfn[next]){
vis[now]=false;
dfs(now,next);
if(dfn[now]<low[next]){
a[++ans].from=min(now,next);
a[ans].to=max(now,next);
}
low[now]=min(low[now],low[next]);
}
}
}
bool cmp(edge p,edge q){
if(p.from!=q.from) return p.from<q.from;
return p.to<q.to;
}
int main(){
n=read(),m=read();
memset(vis,true,sizeof(vis));
for(L i=1;i<=m;i++){
L u=read(),v=read();
G[v].push_back(u);
G[u].push_back(v);
}
for(L i=1;i<=n;i++) if(!dfn[i]) dfs(i,i);
sort(a+1,a+ans+1,cmp);
for(L i=1;i<=ans;i++){
printf("%lld %lld\n",a[i].from,a[i].to);
}
return 0;
}

边双:

与强连通分量一样,当遍历完 u 的所有子节点后,若 low[u]=dfn[u],上面的都是边双的元素。

代码如下:

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=5e5+10;
struct edge{
int v,next;
}edges[N*10];
int head[N],idx=2;
int dfn[N],low[N],clk;
int st[N],top;
bool Cut[N*10];
vector<int>ans[N];
int edcc;
void add_edge(int u,int v){
idx++;
edges[idx].v=v;
edges[idx].next=head[u];
head[u]=idx;
return;
}
void tarjan(int u,int fa){
dfn[u]=low[u]=++clk;
st[++top]=u;
for(int i=head[u];i;i=edges[i].next){
int v=edges[i].v;
if(v==fa)continue;
if(!dfn[v]){
tarjan(v,u);
low[u]=min(low[u],low[v]);
if(low[v]>dfn[u]){
Cut[i]=Cut[i^1]=true;
}
}
else{
low[u]=min(low[u],dfn[v]);
}
}
if(low[u]==dfn[u]){
edcc++;
do{
ans[edcc].push_back(st[top]);
}while(st[top--]!=u);
}
return;
}
signed main(){
std::ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
int n,m;
cin>>n>>m;
for(int i=1;i<=m;i++){
int x,y;
cin>>x>>y;
add_edge(x,y);
add_edge(y,x);
}
tarjan(1,-1);
cout<<edcc<<endl;
return 0;
}

点双:

点双即是找到割点后,将割点上的所有元素出栈,并加入这个点双中(包括割点也要加入),但不要将割点出栈(因为有可能割点属于多个点双)。

代码:

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e6+10;
struct edge{
int v,next;
}edges[N];
int head[N],idx;
int n,m;
int low[N],dfn[N],clk;
vector<int>ans[N];
int st[N],top;
bool Cut[N];
int vdcc,root;
void add_edge(int u,int v){
idx++;
edges[idx].v=v;
edges[idx].next=head[u];
head[u]=idx;
return;
}
void tarjan(int u){
dfn[u]=low[u]=++clk;
st[++top]=u;
if(u==root&&(!head[u])){
vdcc++;
ans[vdcc].push_back(u);
return;
}
int child=0;
for(int i=head[u];i;i=edges[i].next){
int v=edges[i].v;
if(!dfn[v]){
tarjan(v);
child++;
low[u]=min(low[u],low[v]);
if(low[v]>=dfn[u]){
if(u!=root||child>1)Cut[u]=true;
vdcc++;
int tmp;
do{
tmp=st[top--];
ans[vdcc].push_back(tmp);
}while(tmp!=v);
ans[vdcc].push_back(u);
}
}
else low[u]=min(low[u],dfn[v]);
}
return;
}
signed main(){
std::ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
cin>>n>>m;
for(int i=1;i<=m;i++){
int x,y;
cin>>x>>y;
if(x==y)continue;
add_edge(x,y);
add_edge(y,x);
}
for(int i=1;i<=n;i++)if(!dfn[i]){
root=i;
tarjan(i);
}
cout<<vdcc<<endl;
return 0;
}

本文作者:Little_corn

本文链接:https://www.cnblogs.com/little-corn/p/18157442

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   Little_corn  阅读(14)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起