tajan的dfs树系列算法:

求解割点,桥,强连通分量,点双联通分量,边双联通分量;

tajan是一个dfs,把一个图变成一个dfs树结构,

dfs树结构,本质是通过一个没有任何要求的dfs把图的边分为:树边和返祖边:

  • 树边:dfs中父节点与其未曾遍历过的子节点间的边,
  • 返祖边:父节点与他的dfs中曾作为该父节点祖先的子节点间的边

在有向图中,除了这二种边外,还有父节点与曾遍历过的子节点间的边,然而这个子节点不是父节点的祖先,

然而这种边在tarjan中没有意义,我们所求的东西用不上她们

伪代码:

深搜(点now){

  更新点now——

    dfs序(dfn)与目前可到dfn最小祖先的dfn(low),标记已经遍历(vis),确认now将是他后继递归的点的祖先(instk),其他

  for(以now为起点的所有边)

    if(边终点to未遍历)

      深搜(to),low[now]=min(low[now],low[to])

    else

      if(instk[to]为真)//无向图可以不存在这个

        low[now]=min(low[now],dfn[to])

  更新instk[now]为假

}

求(无向图)割点,桥:

割点:无向图中,删除之可改变图的连通性的点

  两种:

  • 两个点双连通分量的公共点
  • 两个点双连通分量直接存在一条边连接,这条边的两端点

桥:无向图中,删除之可改变图的连通性的边

  一种:

  两个边双连通分量间的连边;

求法:

割点:这个点儿子中有至少一个的low小于这个点的dfn

桥:该边终点的low=dfn

例题:

hihocoder1183连通性一·割边与割点

code:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int n,m;
struct ss{
int to,next;
}e[];
struct Cl{
int u,v;
}Cedge[];
int first[],num;
int pnu,enu;
int Cpoint[];
int dfn[],low[],vis[];
bool cmp(Cl a,Cl b){
return a.u<b.u||(a.u==b.u&&a.v<b.v);
}
void Input();
void work();
void Output();
void build(int ,int );
void Init();
void dfs_1(int ,int );
void dfs_2(int ,int );
int main()
{
Input();
work();
Output();
return ;
}
void Input(){
int i,j,k;
scanf("%d%d",&n,&m);
for(i=;i<=m;i++){
scanf("%d%d",&j,&k);
build(j,k);build(k,j);
}
}
void work(){
pnu=;enu=;
Init();
dfs_1(,);
Init();
dfs_2(,);
}
void Output(){
int i;
for(i=;i<=enu;i++)
if(Cedge[i].u>Cedge[i].v)
swap(Cedge[i].u,Cedge[i].v);
sort(Cpoint+,Cpoint+pnu+);
sort(Cedge+,Cedge+enu+,cmp);
for(i=;i<=pnu;i++)
printf("%d ",Cpoint[i]);
if(pnu==)
printf("Null");
printf("\n");
for(i=;i<=enu;i++)
printf("%d %d\n",Cedge[i].u,Cedge[i].v);
}
void build(int f,int t){
e[++num].next=first[f];
e[num].to=t;
first[f]=num;
}
void Init(){
memset(dfn,,sizeof(dfn));
memset(low,,sizeof(low));
memset(vis,,sizeof(vis));
num=;
}
void dfs_1(int now,int fa){
int i,p=;
if(now!=)p=;
dfn[now]=low[now]=++num;vis[now]=;
for(i=first[now];i;i=e[i].next)
if(e[i].to!=fa){
if(!vis[e[i].to]){
dfs_1(e[i].to,now);
if(low[e[i].to]>=dfn[now])p++;
if(low[now]>low[e[i].to])
low[now]=low[e[i].to];
}
else
if(low[now]>dfn[e[i].to])
low[now]=dfn[e[i].to];
}
if(p>=)
Cpoint[++pnu]=now;
}
void dfs_2(int now,int fa){
int i;
dfn[now]=low[now]=++num;vis[now]=;
for(i=first[now];i;i=e[i].next)
if(e[i].to!=fa){
if(!vis[e[i].to]){
dfs_2(e[i].to,now);
if(low[now]>low[e[i].to])
low[now]=low[e[i].to];
if(low[e[i].to]>dfn[now])
Cedge[++enu].u=now,Cedge[enu].v=e[i].to;
}
else
if(low[now]>dfn[e[i].to])
low[now]=dfn[e[i].to];
}
}

求(有向图)强连通分量:

强联通分量:有向图中,点的可以相互到达的关系可以传递,于是有这个关系的一组点与其间的边构成一个强连通分量

求法:

每遍历一个点时,使之进栈,

当遍历结束时,

若其low=dfn,则从栈顶到该节点的所有点属于同一分量,且该分量不含其它点,标记她们,并使她们出栈

(now永远不能到达上层的点,于是她与她的子树中没有自成一派的点构成强连通分量,自成一派的点已经出栈了)

例题:

bzoj1051[HAOI2006]受欢迎的牛

强连通分量缩点后讨论无出度点的个数

code:

#include<cstdio>
using namespace std;
struct ss{
int to,next;
}e[];
int first[],num;
int dfn[],low[],vis[],stk[],col[],number;
int numcol[],into[],color;
int max[];
long long f[],x;
int n,m;
void Input();
void work();
void Output();
void build(int ,int );
void tar(int );
void con_poi();
void dfs(int );
int main()
{
Input();
work();
Output();
return ;
}
void Input(){
int i,j,k;
scanf("%d%d%lld",&n,&m,&x);
for(i=;i<=m;i++){
scanf("%d%d",&j,&k);
build(j,k);
}
}
void work(){
int i,j;
color=n;
for(i=;i<=n;i++)
if(!vis[i])
number=,tar(i);
con_poi();
for(i=n+;i<=color;i++)
if(!vis[i])
dfs(i);
}
void Output(){
int i;
long long ans=,ans_=;
for(i=n+;i<=color;i++){
if(max[i]==ans)
(ans_+=f[i])%=x;
if(max[i]>ans)
ans=max[i],ans_=f[i];
}
printf("%lld\n%lld\n",ans,ans_);
}
void build(int f,int t){
e[++num].next=first[f];
e[num].to=t;
first[f]=num;
}
void tar(int now){
int i;
dfn[now]=low[now]=++number;
vis[now]=;stk[++stk[]]=now;
for(i=first[now];i;i=e[i].next)
if(!vis[e[i].to]){
tar(e[i].to);
if(low[now]>low[e[i].to])
low[now]=low[e[i].to];
}
else
if(vis[e[i].to]==&&low[now]>dfn[e[i].to])
low[now]=dfn[e[i].to];
if(dfn[now]==low[now]){
++color;
while(stk[stk[]+]!=now){
col[stk[stk[]]]=color;
vis[stk[stk[]]]=;
++numcol[color];
--stk[];
}
number=stk[];
}
}
void con_poi(){
int i,j;
for(i=;i<=n;i++)
for(j=first[i];j;j=e[j].next)
if(col[i]!=col[e[j].to])
build(col[i],col[e[j].to]),into[col[e[j].to]]++;
}
void dfs(int now){
int i;
f[now]=;max[now]=numcol[now];
for(i=first[now];i;i=e[i].next)
if(!vis[e[i].to]){
vis[e[i].to]=;
if(!f[e[i].to])
dfs(e[i].to);
if(max[now]==max[e[i].to]+numcol[now])
(f[now]+=f[e[i].to])%=x;
if(max[now]<max[e[i].to]+numcol[now]){
max[now]=max[e[i].to]+numcol[now];
f[now]=f[e[i].to];
}
}
for(i=first[now];i;i=e[i].next)
if(vis[e[i].to])
vis[e[i].to]=;
}

bzoj1093[ZJOI2007]最大半连通子图

强连通分量缩点,计算最长路和最长路计数

code:

#include<cstdio>
using namespace std;
struct ss{
int to,next;
}e[];
int first[],num;
int dfn[],low[],vis[],stk[],col[],number;
int numcol[],into[],color;
int max[];
long long f[],x;
int n,m;
void Input();
void work();
void Output();
void build(int ,int );
void tar(int );
void con_poi();
void dfs(int );
int main()
{
Input();
work();
Output();
return ;
}
void Input(){
int i,j,k;
scanf("%d%d%lld",&n,&m,&x);
for(i=;i<=m;i++){
scanf("%d%d",&j,&k);
build(j,k);
}
}
void work(){
int i,j;
color=n;
for(i=;i<=n;i++)
if(!vis[i])
number=,tar(i);
con_poi();
for(i=n+;i<=color;i++)
if(!vis[i])
dfs(i);
}
void Output(){
int i;
long long ans=,ans_=;
for(i=n+;i<=color;i++){
if(max[i]==ans)
ans_+=f[i];
if(max[i]>ans)
ans=max[i],ans_=f[i];
}
printf("%lld\n%lld\n",ans,ans_);
}
void build(int f,int t){
e[++num].next=first[f];
e[num].to=t;
first[f]=num;
}
void tar(int now){
int i;
dfn[now]=low[now]=++number;
vis[now]=;stk[++stk[]]=now;
for(i=first[now];i;i=e[i].next)
if(!vis[e[i].to]){
tar(e[i].to);
if(low[now]>low[e[i].to])
low[now]=low[e[i].to];
}
else
if(vis[e[i].to]==&&low[now]>dfn[e[i].to])
low[now]=dfn[e[i].to];
if(dfn[now]==low[now]){
++color;
while(stk[stk[]+]!=now){
col[stk[stk[]]]=color;
vis[stk[stk[]]]=;
++numcol[color];
--stk[];
}
number=stk[];
}
}
void con_poi(){
int i,j;
for(i=;i<=n;i++)
for(j=first[i];j;j=e[j].next)
if(col[i]!=col[e[j].to])
build(col[i],col[e[j].to]),into[col[e[j].to]]++;
}
void dfs(int now){
int i;
f[now]=;max[now]=numcol[now];
for(i=first[now];i;i=e[i].next)
if(!vis[e[i].to]){
vis[e[i].to]=;
if(!f[e[i].to])
dfs(e[i].to);
if(max[now]==max[e[i].to]+numcol[now])
(f[now]+=f[e[i].to])%=x;
if(max[now]<max[e[i].to]+numcol[now]){
max[now]=max[e[i].to]+numcol[now];
f[now]=f[e[i].to];
}
}
for(i=first[now];i;i=e[i].next)
if(vis[e[i].to])
vis[e[i].to]=;
}

bzoj2438[中山市选2011]杀人游戏

强连通分量缩点,乱搞,具体看代码;

code:

#include<cstdio>
#include<cstring>
using namespace std;
int n,m,p,ans;
struct ss{
int to,next;
}e[];
int first[],num;
int dfn[],low[],vis[],stk[],number;
int col[],color,numcol[];
int toit[];
void Input();
void work();
void Output();
void build(int ,int );
void tar(int );
void dfs_1(int );
int main()
{
Input();
work();
Output();
return ;
}
void Input(){
int i,j,k;
scanf("%d%d",&n,&m);
for(i=;i<=m;i++){
scanf("%d%d",&j,&k);
build(j,k);
}
}
void work(){
int i,j,k;
color=n;
for(i=;i<=n;i++)
if(!vis[i])
number=,tar(i);
for(i=;i<=n;i++)
for(j=first[i];j;j=e[j].next)
if(col[i]!=col[e[j].to])
build(col[i],col[e[j].to]),toit[col[e[j].to]]++;
memset(stk,,sizeof(stk));
for(i=n+;i<=color;i++)
if(!toit[i])
vis[i]++,dfs_1(i);
for(i=n+;i<=color;i++)
if(!toit[i]){
k=(numcol[i]==);
for(j=first[i];j;j=e[j].next)
k&=(vis[e[j].to]>);
p|=k;
ans++;
}
}
void Output(){
printf("%.6lf",1.0-(double)(ans-p)/(double)(n));
}
void build(int f,int t){
e[++num].next=first[f];
e[num].to=t;
first[f]=num;
}
void tar(int now){
int i;
dfn[now]=low[now]=++number;
stk[++stk[]]=now;vis[now]=;
for(i=first[now];i;i=e[i].next)
if(!vis[e[i].to]){
tar(e[i].to);
if(low[now]>low[e[i].to])
low[now]=low[e[i].to];
}
else
if(vis[e[i].to]==&&low[now]>dfn[e[i].to])
low[now]=dfn[e[i].to];
if(dfn[now]==low[now]){
++color;
while(stk[stk[]+]!=now){
col[stk[stk[]]]=color;
vis[stk[stk[]]]=;
++numcol[color];
--stk[];
}
number=stk[];
}
}
void dfs_1(int now){
int i;
for(i=first[now];i;i=e[i].next)
if(!stk[e[i].to]){
stk[e[i].to]=;
vis[e[i].to]++;
if(vis[e[i].to]==)
dfs_1(e[i].to);
}
for(i=first[now];i;i=e[i].next)
if(stk[e[i].to])
stk[e[i].to]=;
}

边双点双下次再说吧;

Tarjan系列1的更多相关文章

  1. Tarjan系列算法总结(hdu 1827,4612,4587,4005)

    tarjan一直是我看了头大的问题,省选之前还是得好好系统的学习一下.我按照不同的算法在hdu上选题练习了一下,至少还是有了初步的认识.tarjan嘛,就是维护一个dfsnum[]和一个low[],在 ...

  2. tarjan系列算法代码小结

    个人使用,可能不是很详细 强联通分量 这里的dfn可以写成low 因为都是在栈中,只要保证该节点的low值不为本身即可 void tarjan(int now) { dfn[now]=low[now] ...

  3. [Tarjan系列] Tarjan算法与有向图的SCC

    前面的文章介绍了如何用Tarjan算法计算无向图中的e-DCC和v-DCC以及如何缩点. 本篇文章资料参考:李煜东<算法竞赛进阶指南> 这一篇我们讲如何用Tarjan算法求有向图的SCC( ...

  4. [Tarjan系列] 无向图e-DCC和v-DCC的缩点

    上一篇讲了如何应用Tarjan算法求出e-DCC和v-DCC. 那么这一篇就是e-DCC和v-DCC的应用之一:缩点. 先讲e-DCC的缩点. 我们把每一个e-DCC都看成一个节点,把所有桥边(x,y ...

  5. [Tarjan系列] Tarjan算法求无向图的双连通分量

    这篇介绍如何用Tarjan算法求Double Connected Component,即双连通分量. 双联通分量包括点双连通分量v-DCC和边连通分量e-DCC. 若一张无向连通图不存在割点,则称它为 ...

  6. [Tarjan系列] Tarjan算法求无向图的桥和割点

    RobertTarjan真的是一个传说级的大人物. 他发明的LCT,SplayTree这些数据结构真的给我带来了诸多便利,各种动态图论题都可以用LCT解决. 而且,Tarjan并不只发明了LCT,他对 ...

  7. Tarjan入门

    Tarjan系列!我愿称Tarjan为爆搜之王! 1.Tarjan求LCA 利用并查集在一遍DFS中可以完成所所有询问.是一种离线算法. 遍历到一个点时,我们先将并查集初始化,再遍历完一个子树之后,将 ...

  8. 学渣乱搞系列之Tarjan模板合集

    学渣乱搞系列之Tarjan模板合集 by 狂徒归来 一.求强连通子图 #include <iostream> #include <cstdio> #include <cs ...

  9. Tarjan水题系列(5):最大半连通子图 [ZJOI2007 luogu P2272]

    题目 大意: 缩点后转为求最长链的长度和最长链的个数 思路: 看懂题就会做系列 长度和个数都可以拓扑排序后DP求得 毕竟是2007年的题 代码: 如下 #include <cstdio> ...

随机推荐

  1. 主机:Think Pad(6475EC7) 64位的Win7上面装CentOS,说VT模式没有被启动,但BIOS里面已经启用了VT-X

    我的主机是ThindPad,型号是6475EC7,就是比较老的型号. 启动vmware出现 二进制转换与此平台长模式不兼容.......: 首先:进入BOIS查看VT是否已经开启(不同型号电脑VT选项 ...

  2. Spring中applicationContext.xml详解

    <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.sp ...

  3. npm install 报错: WARN checkPermissions Missing write access to 解决方案

    经过各种百度搜索,发现这个问题的出现并不是管理员权限的问题,而是之前安装失败了,这个文件已经存在了,再次安装无法覆盖写入的问题. 解决方法: 1.找到node的全局安装路径,一般在nodejs文件夹的 ...

  4. 防止过拟合:L1/L2正则化

    正则化方法:防止过拟合,提高泛化能力 在训练数据不够多时,或者overtraining时,常常会导致overfitting(过拟合).其直观的表现如下图所示,随着训练过程的进行,模型复杂度增加,在tr ...

  5. chainWebpack 和 htmlWebpackPlugin搭配使用

    const HtmlWebpackPlugin = require('html-webpack-plugin'); ... chainWebpack: config => { config .p ...

  6. 【性能测试】:关于loadrunner11录制Chrome浏览器脚本的方法

    有些web端的程序只能通过chrome访问,如果用lr11录制的话,会抓不到包, 现在教一种方法,可以抓到包的,主体思路就是设置代理,让报文流量绕道走一下 一,在LR中的设置: 从菜单Tools-Re ...

  7. 不支持这个操作系统WNT_6.3I_64

    安装winserver2012驱动时,经常会因为版本的关系,出现向后兼容问题: 编辑驱动安装配置ini, 添加向后兼容的标识即可:WNT_6.3I_64= Win81_64 删除system下的程序( ...

  8. Q239 滑动窗口最大值

    给定一个数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧.你只可以看到在滑动窗口 k 内的数字.滑动窗口每次只向右移动一位. 返回滑动窗口最大值. 示例: 输入: nums ...

  9. Mac 10.12安装虚拟机软件VMware Fusion 12

    说明:VMware创建的虚拟机是全平台通用的,如果要在Mac下识别,那么在虚拟机的文件夹后面增加后缀[.vmwarevm] 下载: (链接: https://pan.baidu.com/s/1eSLE ...

  10. 完美原创:centos7.1 从源码升级安装Python3.5.2

    (原创)完美原创:centos7.1 从源码升级安装Python3.5.2 下载Python3.5.2源码:https://www.python.org/downloads/release/pytho ...