连通分量专题

[连通分量专题]

硬着头皮刷了下连通分量。。

强连通就不说了,是最基础的部分;

割点(割顶),就是在无向图中,删掉这个点,使图不连通的点(或者说使得原图连通块数量增加)。

割边(桥),就是在无向图中,删掉这个边,使图不连通的边(或者说使得原图连通块数量增加)。

那么割顶和桥的求法很类似——

我们都采用DFS树的方法,也记录low,dfn等值,其中dfn表示访问的次序,和求强连通分量的算法类似,low表示当前节点及其后代能连会的最早祖先的dfn值。

然后对于当前结点,枚举与其相邻的每条边,这条边可能是树边,反向边,前向边(返祖边),横向边(横叉边)。

而现在,反向边我们不考虑,横向边在过程中相当于不存在,只存在树边和前向边。

如果是树边,则,low[u]=min(low[u],low[v]);

否则,low[u]=min(low[u],dfn[v])。怎么样,是不是和scc很像?

那怎么判断一个点是不是割顶?dfn[u]<=low[v];是不是桥?dfn[u]<low[v],画个图就好理解了。

那么,我们再来探讨双连通。这是困扰我已久的东西。

双连通又分为点双和边双。

点双相当于无向图中的环,一个点双分量中任意两点至少存在两条“点不重复”的路径,点双与点双之间最多就1个公共点,这个点必然是割顶;

边双是什么?一个边双分量中任意两点至少存在两条“边不重复”的路径,同理,桥不属于任何边双,边双与边双之间由桥连接。

从而,我们可以通过找出割顶后找出点双,找出桥后找边双,这是非常合乎情理的。(这就是为什么一道点双的题目,有人写是割顶,有人写是点双,是我太naive了)

来几个例题:

POJ - 3177

题目意思就是求出至少加几条边才能使原图成为一个边双连通分量。

我们先把原图缩点(无向图缩点,注意反向边),然后必然会形成一棵树。

怎么使这棵树成为边双连通分量?显然,我们一定是在叶子节点建边能得到较优的解,那到底怎么建边?

每次找lca最远的两个点(一定是叶子节点啦)连起来,相当于他们路径上所有点都缩到了一起,

最终要建(设子节点有c个)(c+1)/2条边。

code:

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 #include<stack>
 5 using namespace std;
 6 const int N=5005,M=20005;
 7 int n,m,tot;
 8 int lnk[N],nxt[M],son[M];
 9 int dfn[N],low[N],cloc;
10 stack <int> st;
11 bool inst[N];
12 int scc,bel[N],degrs[N];
13 void add(int x,int y) {
14     nxt[++tot]=lnk[x],son[tot]=y,lnk[x]=tot;
15 }
16 bool jug(int x,int y){
17     if ((x&1)&&y==x+1) return 1;
18     if (!(x&1)&&y==x-1) return 1;
19     return 0;
20 }
21 void tarjan(int x,int fa) {
22     dfn[x]=low[x]=++cloc,inst[x]=1,st.push(x);
23     for (int j=lnk[x]; j; j=nxt[j])
24         if (!jug(j,fa)) {
25             if (!dfn[son[j]]) {
26                 tarjan(son[j],j);
27                 low[x]=min(low[x],low[son[j]]);
28             }
29             else if (inst[son[j]]) {
30                 low[x]=min(low[x],dfn[son[j]]);
31             }
32         }
33     if (low[x]==dfn[x]) {
34         scc++; int y;
35         do {
36             y=st.top(),st.pop(),bel[y]=scc,inst[y]=0;
37         }while (y!=x);
38     }
39 }
40 int main() {
41     scanf("%d%d",&n,&m),tot=0,scc=0;
42     for (int i=1; i<=m; i++) {
43         int x,y; scanf("%d%d",&x,&y);
44         add(x,y),add(y,x);
45     }
46     for (int i=1; i<=n; i++) if (!dfn[i]) tarjan(i,-1);
47     for (int i=1; i<=n; i++)
48         for (int j=lnk[i]; j; j=nxt[j])
49         if (bel[i]!=bel[son[j]]) degrs[bel[son[j]]]++;
50     int ans=0;
51     for (int i=1; i<=scc; i++) if (degrs[i]==1) ans++;
52     printf("%d",(ans+1)/2);
53     return 0;
54 }

POJ -1236

简单缩点,然后统计出入度分别为0的点。

第一问的答案就是入度为0的点的个数,第二问稍微复杂一点。

设入,出度为0的点分别有c1,c2个,则答案为max(c1,c2)个。

怎么做?就是一个源点接在一个汇点上,这个汇点又接在另一个源点上。如果有多余的,那也要接在汇点(源点)上。

code:

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 #include<stack>
 5 using namespace std;
 6 const int N=105;
 7 int tot,lnk[N],nxt[N*N],son[N*N];
 8 int dfn[N],low[N],bel[N];
 9 int din[N],dout[N];
10 int n,scc,cloc;
11 bool inst[N];
12 stack <int> st;
13 void add(int x,int y) {
14     nxt[++tot]=lnk[x],son[tot]=y,lnk[x]=tot;
15 }
16 void tarjan(int x) {
17     low[x]=dfn[x]=++cloc,inst[x]=1,st.push(x);
18     for (int j=lnk[x]; j; j=nxt[j])
19         if (!dfn[son[j]]) {
20             tarjan(son[j]),low[x]=min(low[x],low[son[j]]);
21         } else if (inst[son[j]])
22             low[x]=min(low[x],dfn[son[j]]);
23     if (low[x]==dfn[x]) {
24         scc++;
25         for (int y=st.top(); ; y=st.top()) {
26             bel[y]=scc,inst[y]=0,st.pop();
27             if (y==x) return;
28         }
29     }
30 }
31 int main() {
32     scanf("%d",&n);
33     for (int i=1; i<=n; i++) {
34         int j; scanf("%d",&j);
35         for (; j; scanf("%d",&j)) add(i,j);
36     }
37     for (int i=1; i<=n; i++)
38         if (!dfn[i]) tarjan(i);
39     if (scc==1) {puts("1"),puts("0"); return 0;}
40     for (int i=1; i<=n; i++)
41         for (int j=lnk[i]; j; j=nxt[j])
42         if (bel[i]!=bel[son[j]])
43             dout[bel[i]]++,din[bel[son[j]]]++;
44     int cnt1=0,cnt2=0;
45     for (int i=1; i<=scc; i++) cnt1+=(din[i]==0);
46     for (int i=1; i<=scc; i++) cnt2+=(dout[i]==0);
47     printf("%d\n%d",cnt1,max(cnt1,cnt2));
48     return 0;
49 }

POJ - 3694

题意就是给你初始的一张图,然后不断建边,每次建完问你目前桥的数量。

显然,不能建一条tarjan一次,实在太慢。但我们肯定要预先知道哪些原边是桥,有几条。

那我们要做一次tarjan,会形成一棵DFS树。那么,如果要建一条(x,y)的边,则相当于

x->lca(x,y)和y->lca(x,y)的边都变成了不是桥的边。有了这个小优化我们就能跑过去了。

code:

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 #define Ms(a,x) memset(a,x,sizeof a)
 5 using namespace std;
 6 const int N=100005,M=500005;
 7 int tot,lnk[N],nxt[M],son[M];
 8 int dfn[N],low[N],fa[N];
 9 int n,m,cloc,b;
10 bool isb[M],vis[M];
11 inline int read() {
12     int x=0; char ch=getchar();
13     while (ch<‘0‘||ch>‘9‘) ch=getchar();
14     while (ch>=‘0‘&&ch<=‘9‘)
15         x=(x<<3)+(x<<1)+ch-‘0‘,ch=getchar();
16     return x;
17 }
18 void add(int x,int y) {
19     nxt[++tot]=lnk[x],son[tot]=y,lnk[x]=tot;
20 }
21 void tarjan(int x,int ff) {
22     low[x]=dfn[x]=++cloc,fa[x]=ff;
23     for (int j=lnk[x]; j; j=nxt[j]) {
24         int y=son[j];
25         if (!dfn[y]) {
26             vis[j]=vis[j^1]=1,tarjan(y,x);
27             low[x]=min(low[x],low[y]);
28             if (dfn[x]<low[y]) isb[y]=1,b++;
29         }else if (dfn[y]<dfn[x]&&!vis[j]) {
30             vis[j]=vis[j^1]=1;
31             low[x]=min(low[x],dfn[son[j]]);
32         }
33     }
34 }
35 int calc(int x,int y) {
36     if (dfn[x]<dfn[y]) swap(x,y);
37     while (dfn[x]>dfn[y]) {
38         if (isb[x]) b--,isb[x]=0;
39         x=fa[x];
40     }
41     while (x!=y) {
42         if (isb[y]) b--,isb[y]=0;
43         y=fa[y];
44     }
45     return b;
46 }
47 int main() {
48     int cas=0;
49     while (scanf("%d%d",&n,&m)&&(n+m>0)) {
50         if (cas) puts("");
51         printf("Case %d:\n",++cas),tot=1;
52         Ms(lnk,0),Ms(nxt,0);
53         for (int i=1; i<=m; i++) {
54             int x=read(),y=read();
55             add(x,y),add(y,x);
56         }
57         Ms(dfn,0),Ms(isb,0),Ms(fa,0);
58         cloc=b=0,tarjan(1,0);
59         for (int Q=read(); Q; Q--) {
60             int x=read(),y=read();
61             printf("%d\n",calc(x,y));
62         }
63     }
64     return 0;
65 }

POJ - 2942

未完成。

时间: 2024-11-08 23:15:34

连通分量专题的相关文章

专题训练之强连通分量和2-sat

tarjan模板 1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 using namespace std; 5 const int maxn=20010; 6 const int maxm=50010; 7 struct edge{ 8 int to,nxt; 9 }edge[maxm]; 10 int head[maxn],tot; 11 int low[maxn],dfn[maxn],st

连通图专题汇总

联通图课件 建议学习过程,先简单看了解课件,然后看模版代码,上面会有详细注释,以及我第一做这些代码时候遇到的问题. 无向连通图求 桥和割点 割点:无向连通图中,如果删除某点后,图变成不连通了,则称该点为割点. 桥:   无向连通图中,如果删除某条边后,图变成不连通了,则该边为桥. 注:桥和割点是无向图中的概念. 求桥: 在求割点的基础上吗,假如一个边没有重边(重边 1-2, 1->2 有两次,那么 1->2 就是有两条边了,那么 1->2就不算是桥了). 当且仅当 (u,v) 为父子边,

Kuangbin Flying 6最小生成树专题

先说算法:解释算法思想,可以直接从底下的代码复制作为模版 1.Prim.http://baike.baidu.com/link?url=A_L0v3P9Fqk_cmIGZYzA_hFRSOcCGHF8HYISu8HPjmihFhZ_V22oB3agYXCOYI2dY-SELII_ACQaEh5wK7Bmxq 2.Kruskal.http://baike.baidu.com/view/247951.htm 相信百度百科比我讲得绝对好多了. 图论最主要是建图的思想,然后就是上bin神的模版 A -

【团购巨划算】韩立刚老师门徒级学习专题,只此一次的超大优惠福利

Q:韩立刚老师是谁? A:韩老师是51CTO金牌讲师(最高级别),也是微软最有价值专家MVP.微软企业护航专家.<计算机网络原理>一书作者 讲师主页:http://edu.51cto.com/lecturer/400469.html Q:门徒级学习专题是什么? A:韩立刚老师门徒级课程专题(Windows Server+网络安全+数据库) 韩老师从2013年至今,根据企业对IT运维人才的技术要求,录制视频教程49 门,时长达581小时50分钟.旨在从0起点培养企业高端IT人才,让你在企业IT部

c指针-专题

六---指针 内存和地址怎么理解呢? 机器中有一些位置,每一个位置被称为[字节]/byte,许多现代机器上,每个字节包含8个位.更大内存单位[字],通常包含2个或4个字节组成. 一个字包含4个字节,它的地址是什么? 他仍然只有一个地址,是最左边还是最右边的那个字节的位置,取决于机器. 机器事实-关于整型的起始位置: 在要求边界对齐(boundaryalignment)的机器上,整型存储的起始位置只能是某些特定的字节,通常是2或4的倍数. 变量名和地址关系? 所有高级语言的特性之一,就是通过名字而

无线安全专题_破解篇03--打造个人字典

上一篇讲解了如何通过Kali破解Pin码,今天继续无线安全专题破解篇的第三讲:打造个人字典.通过第一课,我们知道想要破解WPA加密,需要一个强大的字典.字典的强大直接决定了破解的可能性,废话不多说,咱们就学习一下怎么使用kali中的工具生打造个人字典.  一.crunsh工具介绍 今天主要说的是crunsh这款工具,专门用来生成字典. 命令参数: -b              #体积大小,比如-b 20mib 或者 -b 20kib -c              #密码个数(行数),比如80

最短路径算法专题1----弗洛伊德

由于最短路径算法我认为比较重要,所以分成几个专题来慢慢细化去磨它,不能一口气吃个胖子嘛. 首先在说算法之前,先说清楚什么叫做最短路径. 题目一般会给你一张图,然后告诉你很多地方,然后告诉你各个地方之间的路程有多远,要你求出,两点间的最短距离,注意,题目给出的两点间的距离未必是最短的,可能通过第三个点转换之后达到更短.实际其实也是这样的,有时候两个地方并没有直线的道路只有曲线的绕路. 算法的思路: 1.用二维数组列出所有的距离,达到不了的用最大距离表示,如9999999 2.循环数组上面的每一个点

Cocos3D专题课程源码地址

源码地址 https://github.com/super626/LessonDemo cocos2d引擎版本3.7以上 下载后放置在LessonDemo/cocos2d目录下 MAC配置文件 LessonDemo/proj.ios_mac/LessonDemo.xcodeproj,直接用Xcode打开(Xcode5.1以上).编译桌面版和iOS版. win32配置文件 LessonDemo/proj.win32/LessonDemo.sln,用Visual Studio 2012以上版本打开,

Android基础入门教程——10.12 传感器专题(3)——加速度-陀螺仪传感器

Android基础入门教程--10.12 传感器专题(3)--加速度/陀螺仪传感器 标签(空格分隔): Android基础入门教程 本节引言: 本节继续来扣Android中的传感器,本节带来的是加速度传感器(Accelerometer sensor)以及 陀螺仪传感器(Gyroscope sensor),和上一节的方向传感器一样有着x,y,z 三个轴, 还是要说一点:x,y轴的坐标要和绘图那里的x,y轴区分开来!传感器的是以左下角 为原点的!x向右,y向上!好的,带着我们的套路来学本节的传感器吧