Kosoraju

SCC总数及记录SCC所需要的最少边情况

#include<cstdio>
const int N = ;
int scc_cnt = ;
int Case, n, m, i, x, y, cnt, use[N], g[][N], nxt[][N], v[][N], ed, q[N], t, vis[N], e[N][];
inline void add(int x, int y)
{
v[][++ed] = y;
nxt[][ed] = g[][x];
g[][x] = ed;
v[][ed] = x;
nxt[][ed] = g[][y];
g[][y] = ed;
}
void dfs1(int x)
{
vis[x] = ;
for (int i = g[][x]; i; i = nxt[][i])
if (!vis[v[][i]])
{
use[i] = , dfs1(v[][i]);
}
q[++t] = x;
}
void dfs2(int x)
{
vis[x] = ;
for (int i = g[][x]; i; i = nxt[][i])
if (vis[v[][i]])
{
use[i] = , dfs2(v[][i]);
}
}
int main()
{
scanf("%d", &Case);
while (Case--)
{
scc_cnt = ;
scanf("%d%d", &n, &m);
for (ed = , i = ; i <= n; i++)
{
vis[i] = g[][i] = g[][i] = ;
}
for (i = ; i <= m; i++)
{
scanf("%d%d", &x, &y);
e[i][] = x, e[i][] = y;
add(x, y);
use[i] = ;
}
for (t = , i = ; i <= n; i++)
if (!vis[i])
{
dfs1(i);
}
for (i = n; i; i--)
if (vis[q[i]])
{
scc_cnt++;
dfs2(q[i]);
}
printf("%d\n",scc_cnt);
// cnt = n * 2;
// for (i = 1; i <= m; i++)if (use[i])
// {
// cnt--;
// }
// for (i = 1; i <= m; i++)if (!use[i] && cnt > 0)
// {
// use[i] = 1, cnt--;
// }
// for (i = 1; i <= m; i++)if (!use[i])
// {
// printf("%d %d\n", e[i][0], e[i][1]);
// }
}
}

Tarjan

转自:

https://www.cnblogs.com/stxy-ferryman/p/7779347.html

https://blog.csdn.net/sentimental_dog/article/details/53790582

用处:

1.有向图强连通分量   (一个有向图是强连通的当且仅当G中有一个回路,它至少包含每个节点一次)

2.无向图双连通分量(割点,桥)

3.离线LCA

割顶:若去掉一个点和与这个点相连的边后,图不再连通,则这个点是割顶。
​  求法:若节点uu存在一棵子树vv满足vv中所有节点的回边都指向uu及以下的节点(即low[v]≥pre[u]low[v]≥pre[u]),则uu是割顶;所以一次DFS即可求出所有割顶。但注意一个特殊情况!根节点是割顶当且仅当它的子节点数严格大于1。

:去掉这条边,图不再连通。
  求法:若节点uu存在一棵子树vv满足vv中所有节点的回边都指向uu以下(不含uu)的节点(即low[v]>pre[u]low[v]>pre[u]),则边(u,v)(u,v)是桥;所以一次DFS即可求出所有桥。

BCC(点-双连通分量):连通分量中任意两点间都至少有两条“点不重复路径”。
  求法:点双等价于不含割顶的极大子图。但一个问题是割顶本身可能属于多个点双。解决方法是把分开,即保存一个边的栈。

eBCC(边-双连通分量):连通分量中任意两点间都至少有两条“边不重复路径”。
  求法:边双等价于不含桥的极大子图。一条点不可能分属多个边双(否则可以将这些个边双合并)。所以一次DFS求出所有桥,再来一次DFS把边双分开即可(DFS到桥就不走)。

前向星版SCC

void tarjan(int x)
{
dfn[x] = ++deep;
low[x] = deep;
visit[x] = ;
sta[++top] = x;
for (int i = Head[x]; i; i = nxt[i])
{
int v = to[i];
if (!dfn[v])
{
tarjan(v);
low[x] = min(low[x], low[v]);
}
else
{
if (visit[v])
{
low[x] = min(low[x], low[v]);
}
}
}
if (dfn[x] == low[x])
{
color[x] = ++colorsum;
visit[x] = ;
while (sta[top] != x)
{
color[sta[top]] = colorsum;
visit[sta[top--]] = ;
}
top--;
}
}

[USACO06JAN]The Cow Prom

算强连通分量中点数不小于1的个数

/*#include<cstring>#include<algorithm>#include<queue>#include<vector>#include<cstdio>#include<cmath>#include<iostream>*/
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = ;
vector<int> gra[maxn];
int dfn[maxn];//表示这个点在dfs的时候是第几个搜到的;
int low[maxn];//表示这个点及其子节点连的所有点里dfn的最小值
int sta[maxn];//存着当前所有可能能构成强连通分量的点
int visit[maxn];//表示一个点目前是否在sta中
int cnt[maxn];//各个强连通分量中含点的数目
int color[maxn];//表示一个点属于哪个强连通分量
int deep;/*表示从前有多少个点被搜到*/
int top;/*sta目前的大小*/
int colorsum = ;/*目前强连通分量的数目*/
int ans = ;
int n, m;
void tarjan(int x)
{
dfn[x] = ++deep;
low[x] = deep;
visit[x] = ;
sta[++top] = x;
int sz = gra[x].size();
for (int i = ; i < sz; i++)
{
int to = gra[x][i];
if (!dfn[to])
{
tarjan(to);
low[x] = min(low[x], low[to]);
}
else
{
if (visit[to])
{
low[x] = min(low[x], low[to]);
}
}
}
if (dfn[x] == low[x])
{
color[x] = ++colorsum;
visit[x] = ;
while (sta[top] != x)
{
color[sta[top]] = colorsum;
visit[sta[top--]] = ;
}
top--;
}
}
int main()
{
cin >> n >> m;
int from, to;
for (int i = ; i <= m; i++)
{
cin >> from >> to;
gra[from].push_back(to);
}
for (int i = ; i <= n; i++)
{
if (!dfn[i])
{
tarjan(i);
}
}
for (int i = ; i <= n; i++)
{
cnt[color[i]]++;
}
for (int i = ; i <= colorsum; i++)
{
if (cnt[i] > )
{
ans++;
}
}
cout << ans << endl;
}

poj2186 Popular Cows

缩点后求出度为0的点的个数(如果求得的点为缩点后的超级点需要特判)

/*#include<cstring>#include<algorithm>#include<queue>#include<vector>#include<cstdio>#include<cmath>#include<iostream>*/
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = ;
vector<int> gra[maxn];
int du[maxn];
int dfn[maxn];//表示这个点在dfs的时候是第几个搜到的;
int low[maxn];//表示这个点及其子节点连的所有点里dfn的最小值
int sta[maxn];//存着当前所有可能能构成强连通分量的点
int visit[maxn];//表示一个点目前是否在sta中
int cnt[maxn];//各个强连通分量中含点的数目
int color[maxn];//表示一个点属于哪个强连通分量
int deep;/*表示从前有多少个点被搜到*/
int top;/*sta目前的大小*/
int colorsum = ;/*目前强连通分量的数目*/
int ans = ;
int n, m;
void tarjan(int x)
{
dfn[x] = ++deep;
low[x] = deep;
visit[x] = ;
sta[++top] = x;
int sz = gra[x].size();
for (int i = ; i < sz; i++)
{
int to = gra[x][i];
if (!dfn[to])
{
tarjan(to);
low[x] = min(low[x], low[to]);
}
else
{
if (visit[to])
{
low[x] = min(low[x], low[to]);
}
}
}
if (dfn[x] == low[x])
{
color[x] = ++colorsum;
visit[x] = ;
while (sta[top] != x)
{
color[sta[top]] = colorsum;
visit[sta[top--]] = ;
}
top--;
}
}
int main()
{
int temp = ;
cin >> n >> m;
int from, to;
for (int i = ; i <= m; i++)
{
cin >> from >> to;
gra[from].push_back(to);
}
for (int i = ; i <= n; i++)
{
if (!dfn[i])
{
tarjan(i);
}
}
for (int i = ; i <= n; i++)
{
int len = gra[i].size();
for (int j = ; j < len; j++)
{
int to = gra[i][j];
if (color[to] != color[i]) //如果缩点后两个点不在一个强连通分量内
{
du[color[i]]++;
}
}
cnt[color[i]]++;
}
for (int i = ; i <= colorsum; i++)
{
if (temp > )
{
cout << << endl;
return ;
}
if (du[i] == )
{
temp++;
ans = cnt[i];
}
}
cout << ans << endl;
}

洛谷3388 求割点并输出模板题(附带桥输出)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = ;
vector<int> gra[maxn];
int dfn[maxn];//表示这个点在dfs的时候是第几个搜到的;
int low[maxn];//表示这个点及其子节点连的所有点里dfn的最小值
int iscut[maxn];//表示这个点是否是割点
int deep;/*表示从前有多少个点被搜到*/
int ans = ;
int n, m;
int tarjan(int x, int pre)
{
int child = ;
int lowx;
lowx = dfn[x] = ++deep;
int len = gra[x].size();
for (int i = ; i < len; i++)
{
int to = gra[x][i];
if (!dfn[to])
{
child++;
int lowto = tarjan(to, x);
lowx = min(lowx, lowto);
if (lowto >= dfn[x])
{
iscut[x] = ;
}
// if (lowto > dfn[x])
// {
// cout << "bridge " << x << " " << to << endl;
// }
}
else
{
if (to != pre && dfn[to] < dfn[x])
{
lowx = min(lowx, dfn[to]);
}
}
}
if (pre < && child == )
{
iscut[x] = ;
}
low[x] = lowx;
return lowx;
}
int main()
{
int temp = ;
cin >> n >> m;
int from, to;
for (int i = ; i <= m; i++)
{
cin >> from >> to;
gra[from].push_back(to);
gra[to].push_back(from);
}
for (int i = ; i <= n; i++)
{
if (!dfn[i])
{
tarjan(i, -);
}
}
for (int i = ; i <= n; i++)
{
if (iscut[i])
{
ans++;
}
}
cout << ans << endl;
for (int i = ; i <= n; i++)
{
if (iscut[i])
{
cout << i << " ";
}
}
cout << endl;
}

Codeforces 962 F

求点双连通分量中的边

/*Huyyt*/
#include<bits/stdc++.h>
#define mem(a,b) memset(a,b,sizeof(a))
#define pb push_back
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int dir[][] = {{, }, {, }, {, -}, { -, }, {, }, {, -}, { -, -}, { -, }};
const int mod = 1e9 + ;
const int gakki = + + + + 1e9;
const int MAXN = 2e5 + ;
const int MAXM = 2e5 + ;
int to[MAXM << ], nxt[MAXM << ], Head[MAXN], tot = ;
inline void addedge(int u, int v)
{
to[++tot] = v;
nxt[tot] = Head[u];
Head[u] = tot;
}
int n, m;
int dfn[MAXN], low[MAXN], dfs_clock = ;
int BCCcnt = , blong[MAXN], inque[MAXM << ];
int st[MAXN], l = , ans[MAXN], ansnum = ;
bool vis[MAXM << ];
void tarjanBCC(int x, int fa)
{
dfn[x] = low[x] = ++dfs_clock;
for (int i = Head[x]; i; i = nxt[i])
{
int v = to[i];
if (v == fa || vis[i])
{
continue;
}
vis[i] = vis[i ^ ] = ;
st[l++] = i;
if (!dfn[v])
{
tarjanBCC(v, x);
low[x] = min(low[v], low[x]);
if (dfn[x] <= low[v])
{
int now, vnumber = , enumber = ;
BCCcnt++;
while()
{
now = st[--l];
if (blong[to[now]] != BCCcnt)
{
blong[to[now]] = BCCcnt, ++vnumber;
}
if (blong[to[now ^ ]] != BCCcnt)
{
blong[to[now ^ ]] = BCCcnt, ++vnumber;
}
inque[++enumber] = now;
if(now==i)
break;
}
if (vnumber == enumber)
{
for (int i = ; i <= enumber; i++)
{
ans[++ansnum] = inque[i] / ;
}
}
}
}
else
{
low[x] = min(low[x], dfn[v]);
}
}
}
int main()
{
ios_base::sync_with_stdio();
cin.tie();
cin >> n >> m;
int u, v;
for (int i = ; i <= m; i++)
{
cin >> u >> v;
addedge(u, v), addedge(v, u);
}
for (int i = ; i <= n; i++)
{
if (!dfn[i])
{
tarjanBCC(i, -);
}
}
sort(ans + , ans + + ansnum);
cout << ansnum << endl;
for (int i = ; i <= ansnum; i++)
{
cout << ans[i] << " ";
}
return ;
}

附:求边双连通分量中的边

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=1e5+;
int dfn[N],low[N],H[N],nxt[N<<],to[N<<];
int n,m,x,y,cnt,tot=;
bool ib[N],is[N];
void add(int x,int y){
to[++tot]=y;nxt[tot]=H[x];H[x]=tot;
}
void dfs(int u,int fa){
low[u]=dfn[u]=++cnt;
int chi=;
for(int i=H[u];i;i=nxt[i]){
int v=to[i];
if(v==fa) continue;
if(!dfn[v]) {
++chi;
dfs(v,u);
low[u]=min(low[u],low[v]);
if(low[v]>dfn[u]) ib[i]=ib[i^]=;
if(low[v]>=dfn[u]) is[u]=;
}
else low[u]=min(low[u],dfn[v]);
}
if(chi==&&fa==-) is[u]=;
}
int num,bnum,a[N],A[N],ans;
void dfs2(int u,int fa){
++num;for(int i=H[u];i;i=nxt[i]) {
int v=to[i];
if(ib[i]||ib[i^]||v==fa) continue;
ib[i]=ib[i^]=;
a[++bnum]=i>>;
if(!dfn[v]) dfn[v]=,dfs2(v,u);
}
}
int main(){
scanf("%d%d",&n,&m);
for(int i=;i<=m;++i) {
scanf("%d%d",&x,&y);
add(x,y);
add(y,x);
}
for(int i=;i<=n;++i) if(!dfn[i]) dfs(i,-);
for(int i=;i<=n;++i) dfn[i]=;
for(int i=;i<=n;++i) if(!dfn[i]) {
num=bnum=;
dfn[i]=;
dfs2(i,-);
if(num==bnum) for(int j=;j<=num;++j) A[++ans]=a[j];
}
printf("%d\n",ans);
sort(A+,A+ans+);
for(int i=;i<=ans;++i) printf("%d ",A[i]);
}

SCC统计的更多相关文章

  1. CCF CSP 201709-4 通信网络

    CCF计算机职业资格认证考试题解系列文章为meelo原创,请务必以链接形式注明本文地址 CCF CSP 201709-4 通信网络 问题描述 某国的军队由N个部门组成,为了提高安全性,部门之间建立了M ...

  2. POJ 2186 Popular cows(Kosaraju+强联通分量模板)

    题目链接:http://poj.org/problem?id=2186 题目大意:给定N头牛和M个有序对(A,B),(A,B)表示A牛认为B牛是红人,该关系具有传递性,如果牛A认为牛B是红人,牛B认为 ...

  3. Python-基于向量机SVM的文本分类

    项目代码见 Github: 1.算法介绍 2.代码所用数据 详情参见http://qwone.com/~jason/20Newsgroups/ 文件结构 ├─doc_classification.py ...

  4. BZOJ 5450 轰炸 (强连通缩点+DAG最长路)

    <题目链接> 题目大意: 有n座城市,城市之间建立了m条有向的地下通道.你需要发起若干轮轰炸,每轮可以轰炸任意多个城市.但每次轰炸的城市中,不能存在两个不同的城市i,j满足可以通过地道从城 ...

  5. atitit.提升备份文件复制速度(1) -----分析统计问题and解决方案

    atitit.) -----分析统计问题and解决方案 1. 现在的情形 1 2. 硬盘信息 大概50mb/s, 50iops 1 3. 统计小的文件比率 2 4. 复制速度估计.. 2 5. 小文件 ...

  6. bzoj 2707 [SDOI2012]走迷宫(SCC+高斯消元)

    Description Morenan被困在了一个迷宫里.迷宫可以视为N个点M条边的有向图,其中Morenan处于起点S,迷宫的终点设为T.可惜的是,Morenan非常的脑小,他只会从一个点出发随机沿 ...

  7. HDU-2767-tarjan/Kosaraju求scc

    http://acm.hdu.edu.cn/showproblem.php?pid=2767 问最少添加几条边使得图为强连通. tarjan跑一下,然后对强连通分量缩点,找下此时出度为零和入度为零的点 ...

  8. poj 3592 Instantaneous Transference 【SCC +缩点 + SPFA】

    Instantaneous Transference Time Limit: 5000MS   Memory Limit: 65536K Total Submissions: 6204   Accep ...

  9. HDU 3639 SCC Hawk-and-Chicken

    求SCC缩点,统计出每个SCC中的点的个数. 然后统计能到达u的最多的点的个数,可以反向建图,再dfs一遍统计出来. 最后说一下,有必要开一个标记数组,因为测试数据中有重边,结果无限WA. #incl ...

随机推荐

  1. leetcode 83删除排序链表中的重复元素

    /** * Definition for singly-linked list. * struct ListNode { * int val; * ListNode *next; * ListNode ...

  2. Git-Runoob:Git 服务器搭建

    ylbtech-Git-Runoob:Git 服务器搭建 1.返回顶部 1. Git 服务器搭建 上一章节中我们远程仓库使用了 Github,Github 公开的项目是免费的,但是如果你不想让其他人看 ...

  3. Android共享元素场景切换动画的实现

    安卓5.0系统引入了共享元素,能做出非常炫酷的场景切换效果,这让人非常兴奋同时非常蛋疼,因为低版本没法使用啊,所以今天就跟大家分享一下自己写的一个库,其实只有2个文件而已就可以兼容安卓5.0以下的版本 ...

  4. while循环嵌套

    <1>while嵌套的格式 while 条件1: 条件1满足时,做的事情1 条件1满足时,做的事情2 条件1满足时,做的事情3 ...(省略)... while 条件2: 条件2满足时,做 ...

  5. JavaEE-实验三 Java数据库高级编程

    该博客仅专为我的小伙伴提供参考而附加,没空加上代码具体解析,望各位谅解 1.在MySQL中运行以下脚本 CREATE DATABASE mydatabase; USE mydatabase; CREA ...

  6. flutter flutter_swiper使用

    flutter_swiper flutter最强大的siwiper, 多种布局方式,无限轮播,Android和IOS双端适配. 更多详情信息请移步:https://blog.csdn.net/u011 ...

  7. redis cluster 集群 安装 配置 详解

    redis cluster 集群 安装 配置 详解 张映 发表于 2015-05-01 分类目录: nosql 标签:cluster, redis, 安装, 配置, 集群 Redis 集群是一个提供在 ...

  8. java:LeakFilling(JSP,servlet,Ajax,Json,Path)

    1. request.setAttribute("test", "测试");       request.getRequestDispatcher(" ...

  9. java:Cookie(常用操作),Cookie和Session免登录实例

     1.常用操作: package cn.zzsxt.lee.web.cookie; import java.io.IOException; import javax.servlet.ServletEx ...

  10. 【HANA系列】SAP HANA XS Administration Tool登录参数设置

    公众号:SAP Technical 本文作者:matinal 原文出处:http://www.cnblogs.com/SAPmatinal/ 原文链接:[HANA系列]SAP HANA XS Admi ...