Tarjan系列1
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
例题:
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永远不能到达上层的点,于是她与她的子树中没有自成一派的点构成强连通分量,自成一派的点已经出栈了)
例题:
强连通分量缩点后讨论无出度点的个数
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]=;
- }
强连通分量缩点,计算最长路和最长路计数
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]=;
- }
强连通分量缩点,乱搞,具体看代码;
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的更多相关文章
- Tarjan系列算法总结(hdu 1827,4612,4587,4005)
tarjan一直是我看了头大的问题,省选之前还是得好好系统的学习一下.我按照不同的算法在hdu上选题练习了一下,至少还是有了初步的认识.tarjan嘛,就是维护一个dfsnum[]和一个low[],在 ...
- tarjan系列算法代码小结
个人使用,可能不是很详细 强联通分量 这里的dfn可以写成low 因为都是在栈中,只要保证该节点的low值不为本身即可 void tarjan(int now) { dfn[now]=low[now] ...
- [Tarjan系列] Tarjan算法与有向图的SCC
前面的文章介绍了如何用Tarjan算法计算无向图中的e-DCC和v-DCC以及如何缩点. 本篇文章资料参考:李煜东<算法竞赛进阶指南> 这一篇我们讲如何用Tarjan算法求有向图的SCC( ...
- [Tarjan系列] 无向图e-DCC和v-DCC的缩点
上一篇讲了如何应用Tarjan算法求出e-DCC和v-DCC. 那么这一篇就是e-DCC和v-DCC的应用之一:缩点. 先讲e-DCC的缩点. 我们把每一个e-DCC都看成一个节点,把所有桥边(x,y ...
- [Tarjan系列] Tarjan算法求无向图的双连通分量
这篇介绍如何用Tarjan算法求Double Connected Component,即双连通分量. 双联通分量包括点双连通分量v-DCC和边连通分量e-DCC. 若一张无向连通图不存在割点,则称它为 ...
- [Tarjan系列] Tarjan算法求无向图的桥和割点
RobertTarjan真的是一个传说级的大人物. 他发明的LCT,SplayTree这些数据结构真的给我带来了诸多便利,各种动态图论题都可以用LCT解决. 而且,Tarjan并不只发明了LCT,他对 ...
- Tarjan入门
Tarjan系列!我愿称Tarjan为爆搜之王! 1.Tarjan求LCA 利用并查集在一遍DFS中可以完成所所有询问.是一种离线算法. 遍历到一个点时,我们先将并查集初始化,再遍历完一个子树之后,将 ...
- 学渣乱搞系列之Tarjan模板合集
学渣乱搞系列之Tarjan模板合集 by 狂徒归来 一.求强连通子图 #include <iostream> #include <cstdio> #include <cs ...
- Tarjan水题系列(5):最大半连通子图 [ZJOI2007 luogu P2272]
题目 大意: 缩点后转为求最长链的长度和最长链的个数 思路: 看懂题就会做系列 长度和个数都可以拓扑排序后DP求得 毕竟是2007年的题 代码: 如下 #include <cstdio> ...
随机推荐
- php 内存分配新
https://yq.aliyun.com/articles/38307 https://yq.aliyun.com/ziliao/132720 http://blog.liyiwei.cn/%E3% ...
- P5038 [SCOI2012]奇怪的游戏
题目链接 题意分析 首先我们需要求的是统一以后的值\(x\) 并且一般的棋盘操作我们都需要黑白染色 那么对于棋盘格子是偶数的情况的话 答案是存在单调性的 因为如果统一之后 两两搭配还是可以再加一个的 ...
- abp angular 前端权限控制
import { AppComponentBase } from '@shared/app-component-base'; this.permission.isGranted(menuItem.pe ...
- 在微信移动端input file拍照或从相册选择照片后会自动刷新页面退回到一开始网站进入的页面
<input type="file" accept="image/*"/> 调用打开摄像头后,聚焦后拍照,点击确认,这时页面会出现刷新动作,然后回退 ...
- [转] Nexus OSS 3.xx 体验
[From] https://blog.csdn.net/qq250782929/article/details/51605965 Nexus Manager OSS 3.0 —Maven Repos ...
- (转)如何在CentOS / RHEL 7上安装Elasticsearch,Logstash和Kibana(ELK)
原文:https://www.howtoing.com/install-elasticsearch-logstash-and-kibana-elk-stack-on-centos-rhel-7 如果你 ...
- (转)MySQL高可用架构之MHA
MySQL高可用架构之MHA 原文:http://www.cnblogs.com/gomysql/p/3675429.html 简介: MHA(Master High Availability)目前 ...
- java.util.Collections.synchronizedSet()方法的使用
下面的例子显示java.util.Collections.synchronizedSet()方法的使用 package com.; import java.util.*; public class C ...
- php 判断字符串之间包含关系
之前常用stristr , strpos判断. 因为处理1000W * 1000W级别,循环就是漫长漫长... 在此,对stristr, strpos, explode判断字符串包含关系处理速度对比 ...
- linux 将一个服务器上的文件或者文件夹复制到另一台服务器上
使用scp将一个Linux系统中的文件或文件夹复制到另一台Linux服务器上 复制文件或文件夹(目录)命令: 一.复制文件: (1)将本地文件拷贝到远程 scp 文件名 用户名@计算机IP或者计 ...