diker

tarjan好神奇啊。

一、最大强连通分量问题

1、一些定义:

<1>强连通:在有向图G中,设有两个点u、v,发现由u有一条路可以到v,由v也有一条路可以到u,则u、v强连通。

<2>强连通图:每两个点都强连通的有向图叫强连通图。

<3>强连通分量有向图极大强连通子图。

2、思路:
当在一个有向图中找到了一条从u到u的回路,则在该路径上的所有点和u可以构成一个强连通子图,因为环上任意两点都可以互相到达,那么所有从u到u的回路上的所有点和u构成一个强连通分量。 (personal view)

3、算法:最简单的想法是dfs找回路,记录路径上的点,但图太大时时间空间上不够,需要加一些优化。tarjan算法就是基于dfs,使用栈,标记数组等来进行优化,使得时间复杂度在O(n)。

用到的两个辅助数组:

dfn[u]:记录点u是第几个遍历到的(只是标记遍历先后顺序)【时间戳】因为回路上的点本来是没有顺序可言的,dfn人为加了一个顺序。

low[u]:记录点u所在回路中可以到达的点中最小的时间戳
用栈储存强联通分量,时间戳越小越早入栈。

用vis[u]数组来记录u是否在栈中。

算法的具体步骤:

tarjan(u){//当遍历到u点时dfn[u]=low[u]=++ind;//先初始化u点的dfn和low都等于当前的时间戳s.push(u);//将u压进栈vis[u]=1;//标记u在栈中for(v:Edge(u)){//搜索u可以到达的所有点vif(!dfn[v]) tarjan(v);//如果v点还没有被遍历过,遍历v点if(vis[v]) low[u]=min(low[u],low[v]);//如果v点还在栈中,更新low[u]的值//因为如果v还在栈中则代表v在u的一条回路上,那么v可以到达的最早遍历的点(low[v])u点也可以到达//所以low[u]在low[u]、low[v]中取一个最小的//而如果v已经不在栈中了,说明v点到不了v点之前的任何点了,已经找完了v点的所以回路并且都已出栈}if(low[u]==dfn[u])//u所能到达的最早遍历的点就是u本身,而且u的回路全部都搜索完了//那么u点及栈中在u点上(比u后入栈)的点构成一个极大强连通分量do{vis[s.top()]=0;//标记不在栈中了s.pop();//出栈}while(vis[u]);//直到u点出栈为止//如果low[u]!=dfn[u],代表u是dfn[]==low[u]的点的回路上的一点,而这个点还没有拓展完所有回路
}

T1:洛谷P1726 上白泽慧音
题目链接
题目描述
在幻想乡,上白泽慧音是以知识渊博闻名的老师。春雪异变导致人间之里的很多道路都被大雪堵塞,使有的学生不能顺利地到达慧音所在的村庄。因此慧音决定换一个能够聚集最多人数的村庄作为新的教学地点。人间之里由N个村庄(编号为1…N)和M条道路组成,道路分为两种一种为单向通行的,一种为双向通行的,分别用1和2来标记。如果存在由村庄A到达村庄B的通路,那么我们认为可以从村庄A到达村庄B,记为(A,B)。当(A,B)和(B,A)同时满足时,我们认为A,B是绝对连通的,记为<A,B>。绝对连通区域是指一个村庄的集合,在这个集合中任意两个村庄X,Y都满足<X,Y>。现在你的任务是,找出最大的绝对连通区域,并将这个绝对连通区域的村庄按编号依次输出。若存在两个最大的,输出字典序最小的,比如当存在1,3,4和2,5,6这两个最大连通区域时,输出的是1,3,4。
输入格式:
第1行:两个正整数N,M
第2…M+1行:每行三个正整数a,b,t, t = 1表示存在从村庄a到b的单向道路,t = 2表示村庄a,b之间存在双向通行的道路。保证每条道路只出现一次。(N <= 5000且M <= 50000)
输出格式:
第1行: 1个整数,表示最大的绝对连通区域包含的村庄个数。
第2行:若干个整数,依次输出最大的绝对连通区域所包含的村庄编号。

裸的求最大强连通分量的题(最适合我这种菜鸡练手了)
代码:

#include <iostream>
#include <cmath>
#include <algorithm>
#include <stack>
#define LL long long
#define _for(i,j,k) for(int i=j;i<=k;i++)
#define for_(i,j,k) for(int i=j;i>=k;i--)
using namespace std;
const int maxn = 5e3+5;
const int maxm = 5e4+5;
int n,m,cnt,head[maxn],net[maxm],e[maxm];
int vis[maxn],dfn[maxn],low[maxn],tar[maxn],ma,mark,ind,mi,mar;
stack<int> s;
void add_edge(int u,int v){//邻接表存图  e[++cnt]=v;  net[cnt]=head[u];head[u]=cnt;
}
void tarjan(int u){dfn[u]=low[u]=++ind;s.push(u);vis[u]=1;for(int i=head[u];i;i=net[i]){if(!dfn[e[i]]) tarjan(e[i]);if(vis[e[i]]) low[u]=min(low[u],low[e[i]]); }if(low[u]==dfn[u]){mark++;//mark强连通分量编号int tcnt=0;//当前强连通分量包含的点数do{tar[s.top()]=mark;//tar[i]代表i是编号为tar[i]的强连通分量的点vis[s.top()]=0;s.pop();tcnt++;}while(vis[u]);if(tcnt>ma||(tcnt==ma&&u<mi)){ma=tcnt;//最大强连通分量包含的点数mi=u;//最大强连通分量包含的点中最小的点mar=mark;//最大强连通分量的编号}}
}
int main(){cin>>n>>m;int u,v,t;_for(i,1,m){cin>>u>>v>>t;add_edge(u,v);if(t==2) add_edge(v,u);}mi=n;_for(i,1,n){if(!dfn[i]) tarjan(i);}int kase=0;cout<<ma<<"\n";_for(i,1,n){if(tar[i]==mar){if(kase) cout<<" ";else kase++;//控制输出格式cout<<i;}}return 0;
}

二、割点、割边(桥)

割点: 对于一个连通的无向图,删去某一个点(连同与改点相连的边)后,图变得不连通了,该点就为一个割点。

方法是使用tarjan算法:
dfn[u]记录点u是第几个遍历到的,low[u]记录点u所在回路中可以到达的点中最小的时间戳。

假设dfs到u时,u存在一条边可以到达v,而v能到达的最早的点的时间戳比u的时间戳要大或等于(即low[v]>=dfn[u]),那么删去后,就存在包括v在内的一个联通块与u点前面一部分不联通,整个图就不联通了。
但是存在一个特殊情况:u的前面没有点,也就是说u是第一个遍历到的。这种情况下,如果存在两个以上的点满足low[v]>=dfn[u](v是与u相连的点),那么删去u以后图是不联通的。

具体步骤:

isc[maxn];//标记点是否为割点
cnt=0;//记录根结点分支数
tarjan(u,rt){//当遍历到u点时,rt点前联通块的根(最新遍历的点)dfn[u]=low[u]=++ind;//先初始化u点的dfn和low都等于当前的时间戳for(v:Edge(u)){//搜索u可以到达的所有点vif(!dfn[v]){tarjan(v,rt);//如果v点还没有被遍历过,遍历v点low[u]=min(low[u],low[v]);//更新low[u]的值if(u==rt) cnt++;if(low[v]>=dfn[u]&&u!=rt) isc[u]=1;//u是割点}low[u]=min(low[u],dfn[v]);//更新low[u]}if(u==rt&&cnt>=2) isc[u]=1;//根结点的特判
}

割边(桥): 对于一个连通的无向图,删去某一条边后,图变得不连通了,该点就为一个割点。

同样用的tarjan算法,套用上面割点的模板,把 low[v]>=dfn[u] 条件判断的等号去掉(low[v]>dfn[u]),记录边的编号就可以了。(如果用的链表形式的邻接表那么记录边的编号就灰常方便了)

三、双连通分量

点双连通: 若一个无向图中的去掉任意一个节点都不会改变此图的连通性,即不存在割点,则称作点双连通图。

边双连通: 若一个无向图中的去掉任意一条边都不会改变此图的连通性,即不存在割边(桥),则称作边双连通图。

点(边)连通图、点(边)连通分量和上面的强连通图、强连通分量类似。

TBC…

蒟蒻的笔记本二、tarjan相关推荐

  1. 蒟蒻重返c++,学海拾贝(二)

    蒟蒻重返c++,学海拾贝(二)6.27 条件表达式: 格式:<表达式1>?<表达式2>:<表达式3> 条件表达式要求有三个操作对象,"?"和&q ...

  2. 二本蒟蒻的带牌退役感言(感谢两年来的acm经历)

    TP 20年10月 20年 - 21年 寒假 22年开始,大二下 暑假后,怎么就大三了,时间好快 第47届icpc杭州站 尾声 润~ 20年10月 一个高考发挥失常的蒟蒻来到了化大.他带着不甘和兴奋走 ...

  3. [颓废史]蒟蒻的刷题记录

    QAQ蒟蒻一枚,其实我就是来提供水题库的. 以下记录从2016年开始. 1.1 1227: [SDOI2009]虔诚的墓主人 树状数组+离散化 3132: 上帝造题的七分钟 树状数组 二维区间加减+查 ...

  4. 本蒟蒻对于二分图一些定理的理解

    本蒟蒻对于二分图一些定理的理解 先给出一些定理 (常识) 1.对于一个无向图 G,若 G 中的所有回路长度均为偶数,则G为一个二分图. 2.二分图的最小点覆盖 = 最大匹配数. 3.二分图的最大独立集 ...

  5. 蒟蒻的五周总结(解释引用)《挑战》

    一:尺取法: 解释摘自:(12条消息) 尺取法 - 详解 + 例题模板(全)_lxt_Lucia的博客-CSDN博客_尺取法 引用:顾名思义,像尺子一样取一段,借用挑战书上面的话说,尺取法通常是对数组 ...

  6. NOIP 2015 蒟蒻做题记录

    昨天做了noip 2015 的题.因为之前做过几道,最开始做的很快,也都A了.可是子串斗地主运输计划什么的这些没做过的题还是把我恶心的不行QAQ我这个大蒟蒻还是没有A掉..所以说先写一下应该得到的暴力 ...

  7. P1185 绘制二叉树——蒟蒻的暴力模拟

    被此题逼疯,必须发博庆祝 前言 一上午自习然而被一题卡脖子. 建树.删树--行行行,你让我做什么我就做什么,无力吐槽模拟. 一.题目 题目描述 二叉树是一种基本的数据结构,它要么为空,要么由根节点,左 ...

  8. 第十六届全国大学生智能车竞赛(安徽赛区)信标组蒟蒻的想法

    前言:作为一名准大三的软件工程学生,刚刚结束了他的为期十几天的智能车之旅,学习了很多,受益匪浅. 1. 什么是智能车? 印象里面好像就是一个车在赛道上跑,然后一群人在哪里看着它跑,就像遥控车那样简单, ...

  9. 蒟蒻成长之路(持续更新)

    蒟蒻成长之路 (这个玩意只是闲着写写, 写给自己看的) 开始 开始日期:2023年3月23日20:55:24 内容 主要记录一些做题日常和快乐的学校生活 初一:2022~2023 Day1--2023 ...

  10. APIO2020 蒟蒻游记

    2020年是本蒟蒻第一次参加APIO,主要目的是去国赛难度的大赛参观参观 (是学习!不是参观!),而且APIO门槛相对低一些... 因此,这篇文章讲的是像我这样的蒟蒻,花完APIO的5个小时的过程.我 ...

最新文章

  1. C#中三种定时器对象的比较
  2. golang中的可见性
  3. 记一个bug定位与修复过程
  4. 【Spring学习】spring注解自动注入bean
  5. java策略设计模式_Java中的策略设计模式
  6. WAMPServer使用
  7. java dataurl_FileReader生成图片dataurl的分析
  8. python调用dm.dll
  9. 城通网盘文件地址分析器
  10. 最有效率地戒掉晚睡强迫症(熬夜强迫症、假象失眠症等等)
  11. 【python技能树】python程序设计思想
  12. matlab做分数阶差分,分数阶微分方程数值实验MATLAB编码
  13. 【论文阅读·2】”Why Should I Trust You?” Explaining the predictions of Any Classifier
  14. 安卓测试二(Espresso)
  15. 加密市场熊市最后的曙光——Treasure Project(藏宝计划)
  16. Vue项目 课程列表页 跳转 课程详情页 跳转 视频播放页
  17. python字典怎么处理_Python字典的处理
  18. CH9141蓝牙模块与STM32F1无法通信的问题
  19. 大话 AliPay踩的坑
  20. SaaSpace:8种最好的免费图形设计软件

热门文章

  1. T检验三种方法的区分
  2. COOC6.2增加同义词合并无意义词删除等功能
  3. edge浏览器打开html文件路径被拆分,Edge浏览器显示网页排版错位
  4. CyanogenMod编译
  5. python判别性别的代码_根据三围数据判断出用户性别竟是python使用逻辑回归算法搞的鬼!...
  6. Chrome屏蔽广告
  7. matlab中argmax_argmin(matlab中argmin函数)
  8. 汇编语言必看书籍推荐
  9. 强弱类型,动态静态语言比较(JAVA,C,C++,Python,Ruby,PHP,Perl)
  10. Linux下设置网卡速率 降低网卡速度