◆HihoCoder 1183◆
连通性一·割边与割点
□题目□
HihoCoder的题目都这么长吗?(>_<)作者还是不要copy了,直接上网址吧:
戳这里→HihoCoder 1183
□解析□
很明显,这就是一道版题……虽然是板,但是毕竟是比较考验思维的题。
1. 割点与割边
它们的定义非常简单——在一个连通图中,若删去点u会将原图分成2个及以上的连通图,则称u为割点;若删去边F会将原图分成2个及以上的连通图,则称F为割边。
2. DFN图
DFN图是通过用DFS实现的将一个连通图变为一棵树并为所有点按照遍历顺序编号,DFS遍历时不一定要按照从小到大的顺序,按输入顺序即可。
如:
所谓树边就是在DFS中从一个已经访问过的节点到达一个未访问过的节点所通过的边,原连通图中除了树边就是回边,或者可以这样理解——若在遍历中寻找与所在点u相邻的点v时,访问到了一个访问过的节点,且v不是DFN树中u的父亲,则那条边就是回边。
- 树边与回边
定义如前所示,那么我们需要定义如下数组:
vector<int> vec[MAXN+5];
set<int> ans;
struct Fans{int u,v;}fans[MAXN+20000];int n_fans;
int dfn[MAXN+5],fa[MAXN+5],low[MAXN+5];
- vec[] 是vector形式的邻接表(无向);
- ans 是用set存储的割点答案,自动排序、去重;
- fans[] 是结构体存储的割边答案;
- dfn[u] 表示u点在dfs中访问到的点数;
- fa[u] 表示在dfs中第一次访问u点时上一个访问的点,即dfn树中u点的编号;
- low[u] 表示在原图中从u点出发,只经过回边能够访问的所有的点的最小的dfn值;
其中low和dfn是比较重要的两个值——也就是可以通过low和dfn得出该点是否是割点(或者割边)——在dfn树中,u是v的父亲,则若low[v]>=dfn[u],则该点为割点,而若low[v]>=dfn[u],则边 u->v 是一条割边。
上图的值如下:
1 | 2 | 3 | 4 | 5 | 6 | |
---|---|---|---|---|---|---|
dfn[] | 1 | 6 | 2 | 3 | 4 | 5 |
low[] | 1 | 1 | 1 | 3 | 3 | 3 |
这里有low[4]>=dfn[3],low[5]>=dfn[4],所以点3、4是割点,而仅有low[4]>dfn[3],所以3->4是割边。
□代码□
en……代码就在这儿……你们敢直接copy吗? (⊙o⊙)…
/*Lucky_Glass*/
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<set>
#include<fstream>
using namespace std;
#define HaHa freopen("in.txt","r",stdin)
#define MAXN 20000
int n_poi,n_edg,cnt;
vector<int> vec[MAXN+5];
set<int> ans;
struct Fans{int u,v;}fans[MAXN+20000];int n_fans;
int dfn[MAXN+5],fa[MAXN+5],low[MAXN+5];
bool edg_or_poi;
bool cmp(Fans A,Fans B)
{
if(A.u-B.u)
return A.u<B.u;
else
return A.v<B.v;
}
ofstream O("haha.txt");
void DFS(int u)
{
dfn[u]=low[u]=++cnt;
int siz=vec[u].size(),n_chl=0;
for(int i=0;i<siz;i++)
{
int v=vec[u][i];
if(!dfn[v])
{
n_chl++;
fa[v]=u;
DFS(v);
low[u]=min(low[u],low[v]);
if(fa[u]==-1)
{
if(n_chl>=2)
ans.insert(u);
}
else
{
if(edg_or_poi)
{
if(low[v]>dfn[u])
fans[n_fans].u=min(u,v),fans[n_fans++].v=max(u,v);
}
else
{
if(low[v]>=dfn[u])
ans.insert(u);
}
}
}
else
if(v!=fa[u])
low[u]=min(low[u],dfn[v]),O<<0;
}
}
int main()
{
HaHa;
scanf("%d%d",&n_poi,&n_edg);
for(int i=0;i<n_edg;i++)
{
int A,B;scanf("%d%d",&A,&B);
vec[A].push_back(B);
vec[B].push_back(A);
}
fa[1]=-1;
DFS(1);
if(ans.empty()) puts("Null");
for(set<int>::iterator it=ans.begin();it!=ans.end();it++)
it==ans.begin()? true:putchar(' '),printf("%d",*it);
memset(dfn,0,sizeof dfn);
memset(fa,0,sizeof fa);
memset(low,0,sizeof low);
puts("");
edg_or_poi=true;
DFS(1);
sort(fans,fans+n_fans,cmp);
for(int i=1;i<n_fans;i++)
printf("%d %d\n",fans[i].u,fans[i].v);
return 0;
}