强连通

一、模板

(1)tarjan模板

 1 #define N 30100
 2 //N为最大点数
 3 #define M 150100
 4 //M为最大边数
 5 int n, m;//n m 为点数和边数
 6
 7 struct Edge{
 8     int from, to, nex;
 9     bool sign;//是否为桥
10 }edge[M<<1];
11 int head[N], edgenum;
12 void add(int u, int v){//边的起点和终点
13     Edge E={u, v, head[u], false};
14     edge[edgenum] = E;
15     head[u] = edgenum++;
16 }
17
18 int DFN[N], Low[N], Stack[N], top, Time; //Low[u]是点集{u点及以u点为根的子树} 中(所有反向弧)能指向的(离根最近的祖先v) 的DFN[v]值(即v点时间戳)
19 int taj;//连通分支标号,从1开始
20 int Belong[N];//Belong[i] 表示i点属于的连通分支
21 bool Instack[N];
22 vector<int> bcc[N]; //标号从1开始
23
24 void tarjan(int u ,int fa){
25     DFN[u] = Low[u] = ++ Time ;
26     Stack[top ++ ] = u ;
27     Instack[u] = 1 ;
28
29     for (int i = head[u] ; ~i ; i = edge[i].nex ){
30         int v = edge[i].to ;
31         if(DFN[v] == -1)
32         {
33             tarjan(v , u) ;
34             Low[u] = min(Low[u] ,Low[v]) ;
35             if(DFN[u] < Low[v])
36             {
37                 edge[i].sign = 1;//为割桥
38             }
39         }
40         else if(Instack[v]) Low[u] = min(Low[u] ,DFN[v]) ;
41     }
42     if(Low[u] == DFN[u]){
43         int now;
44         taj ++ ; bcc[taj].clear();
45         do{
46             now = Stack[-- top] ;
47             Instack[now] = 0 ;
48             Belong [now] = taj ;
49             bcc[taj].push_back(now);
50         }while(now != u) ;
51     }
52 }
53
54 void tarjan_init(int all){
55     memset(DFN, -1, sizeof(DFN));
56     memset(Instack, 0, sizeof(Instack));
57     top = Time = taj = 0;
58     for(int i=1;i<=all;i++)if(DFN[i]==-1 )tarjan(i, i); //注意开始点标!!!
59 }
60 vector<int>G[N];
61 int du[N];
62 void suodian(){
63     memset(du, 0, sizeof(du));
64     for(int i = 1; i <= taj; i++)G[i].clear();
65     for(int i = 0; i < edgenum; i++){
66         int u = Belong[edge[i].from], v = Belong[edge[i].to];
67         if(u!=v)G[u].push_back(v), du[v]++;
68     }
69 }
70 void init(){memset(head, -1, sizeof(head)); edgenum=0;}  

二、练习

1、【CodeForces 427C】 Checkposts

题意:n(1<=n<=10^5)个城市,m(1<=m<=10^5)条单向的路,现在要放一些保安来管理这n个城市,如果在第i个城市放保安,需要花费a[i](0<=a[i]<=10^9)的钱,如果城市j满足【保安能从i走到j,同时能从j走回到i】那么放在城市i的保安能管理城市j。求在哪些城市放保安能将这n个城市全都治理到,并且所花的钱最少,输出花的钱以及放保安的方案数。

解题思路:模板题,求出每个连通分量的最小花费minx[taj],以及最小花费的个数maxn[taj],花的最少钱是minx[]的和,方案数就是maxn[]的乘积。

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 #define ll __int64
 4 const int mod=1e9+7;
 5 #define N 301000
 6 //N为最大点数
 7 #define M 150100
 8 //M为最大边数
 9 int n, m;//n m 为点数和边数
10
11 struct Edge{
12     int from, to, nex;
13 }edge[N<<1];
14 int head[N], edgenum;
15 void add(int u, int v){//边的起点和终点
16     Edge E={u, v, head[u]};
17     edge[edgenum] = E;
18     head[u] = edgenum++;
19 }
20
21 int DFN[N], Low[N], Stack[N], top, Time;
22 //Low[u]是点集{u点及以u点为根的子树}中
23 //(所有反向弧)能指向的(离根最近的祖先v)的DFN[v]值(即v点时间戳)
24 int taj;//连通分支标号,从1开始
25 int Belong[N];//Belong[i] 表示i点属于的连通分支
26 bool Instack[N];
27 ll pp[N], minx[N], maxn[N];
28 void tarjan(int u ,int fa){
29     DFN[u] = Low[u] = ++ Time ;
30     Stack[top ++ ] = u ;
31     Instack[u] = 1 ;
32
33     for (int i = head[u] ; ~i ; i = edge[i].nex ){
34         int v = edge[i].to ;
35         if(DFN[v] == -1)
36         {
37             tarjan(v , u) ;
38             Low[u] = min(Low[u] ,Low[v]) ;
39         }
40         else if(Instack[v]) Low[u] = min(Low[u] ,DFN[v]) ;
41     }
42     if(Low[u] == DFN[u]){
43         int now;
44         taj ++ ;
45         do{
46             now = Stack[-- top] ;
47             Instack[now] = 0 ;
48             Belong [now] = taj ;
49             if(minx[taj]>pp[now]) minx[taj]=pp[now], maxn[taj]=0;
50             if(minx[taj]==pp[now]) maxn[taj]++;
51         }while(now != u) ;
52     }
53 }
54
55 void tarjan_init(int all){
56     memset(DFN, -1, sizeof(DFN));
57     memset(Instack, 0, sizeof(Instack));
58     memset(minx, 0x3f3f3f3f, sizeof(minx));
59     memset(maxn, 0, sizeof(maxn));
60     top = Time = taj = 0;
61     for(int i=1;i<=all;i++)if(DFN[i]==-1 )tarjan(i, i); //注意开始点标!!!
62 }
63 void init(){memset(head, -1, sizeof(head)); edgenum=0;}
64 int main(){
65         scanf("%d", &n);
66         init();
67         for(int i=1; i<=n; i++) scanf("%d", &pp[i]);
68         scanf("%d", &m);
69         int u, v;
70         for(int i=0; i<m; i++){
71             scanf("%d%d", &u, &v);
72             add(u, v);
73         }
74         tarjan_init(n);
75         ll ans=0, sum=1;
76         for(int i=1; i<=taj; i++){
77             ans+=minx[i];
78             sum = (sum*maxn[i])%mod;
79         }
80         printf("%I64d %I64d\n", ans, sum);
81     return 0;
82 }

时间: 2024-10-19 19:31:17

强连通的相关文章

Kosaraju算法解析: 求解图的强连通分量

1. 定义 连通分量:在无向图中,即为连通子图. 上图中,总共有四个连通分量.顶点A.B.C.D构成了一个连通分量,顶点E构成了一个连通分量,顶点F,G和H,I分别构成了两个连通分量. 强连通分量:有向图中,尽可能多的若干顶点组成的子图中,这些顶点都是相互可到达的,则这些顶点成为一个强连通分量. 上图中有三个强连通分量,分别是a.b.e以及f.g和c.d.h. 2. 连通分量的求解方法 对于一个无向图的连通分量,从连通分量的任意一个顶点开始,进行一次DFS,一定能遍历这个连通分量的所有顶点.所以

POJ 2186 Popular Cows 强连通分量模板

题意 强连通分量,找独立的块 强连通分量裸题 #include <cstdio> #include <cstdlib> #include <cstring> #include <string> #include <algorithm> #include <iostream> using namespace std; const int maxn = 50005; int n, m; struct Edge { int v, next;

USACO network of school 强连通分量

这个题的意思是有一个有向图, 每个顶点可以发送软件到与其相连的顶点上, 现在问1,至少发送给几个顶点能满足所有顶点都收到软件, 2:如果想让这个图变成强连通图,至少添几条边.  特例是给定的图是一个强连通图的话答案是1, 0. 一般情况下我们先将这个图的强连通分量求出来缩成一个点然后统计入度为0的点和出度为0的点的个数, 答案一就是入度为0的点的个数, 答案就是他们两个之间的最大值.代码如下: /* ID: m1500293 LANG: C++ PROG: schlnet */ #include

hdu3861 强连通+最小路径覆盖

题意:有 n 个点,m 条边的有向图,需要将这些点分成多个块,要求:如果两点之间有路径能够互相到达,那么这两个点必须分在同一块:在同一块内的任意两点相互之间至少要有一条路径到达,即 u 到达 v 或 v 到达 u:每个点都只能存在于单独一个块内.问最少需要划分多少块. 首先,对于如果两点之间能够相互到达则必须在同一块,其实也就是在同一个强连通分量中的点必须在同一块中,所以首先就是强连通缩点.然后在同一块内的任意两点之间要有一条路,那么其实就是对于一块内的强连通分量,至少要有一条路径贯穿所有分量.

HDU 3072 Intelligence System (强连通分量)

题目地址:HDU 3072 这题一开始理解错题目意思了..(不得不吐槽一下题目意思确实不好理解..)用的强连通+最小生成树做的...然后错了好多遍...sad..题目意思是从一个给定点向其他所有点通信的最少花费,那么入度为0的点肯定是那个给定点.然后对于其它点,枚举就好了.找一个到他花费最少的点的花费. 代码如下: #include <iostream> #include <string.h> #include <math.h> #include <queue&g

强连通分量(学习心得)

定义:有向图强连通分量:在有向图G中,如果两个顶点vi,vj间(vi>vj)有一条从vi到vj的有向路径,同时还有一条从vj到vi的有向路径,则称两个顶点强连通如果有向图G的每两个顶点都强连通,称G是一个强连通图.有向图的极大强连通子图,称为强连通分量. 求强连通分量: vector<int>pic[maxn]; int dfn[maxn],low[maxn],ans[maxn]; bool ins[maxn]; stack<int>st; int dind=0,block=

UVA-11324 The Largest Clique 【有向图强连通+缩点+DP】

题目链接:https://vjudge.net/problem/UVA-11324 题目大意:给定一张有向图G,求一个结点数最大的结点集,集合中每两个点都至少有一条路径相连(方向任意). 题解: 易知如果一个点被选择,则它所在强连通分量中的其他点也一定要选,如果不选,则其他点也不可选,因此先求出强连通分量,利用缩点创建出另一张有向图G2,每个结点的权值就是该强连通分量的结点数,再DP求解. 代码: 1 #include<bits/stdc++.h> 2 using namespace std;

POJ 2186:Popular Cows(强连通分量)

[题目链接] http://poj.org/problem?id=2186 [题目大意] 给出一张有向图,问能被所有点到达的点的数量 [题解] 我们发现能成为答案的,只有拓扑序最后的SCC中的所有点, 那么我们从其中一个点开始沿反图dfs,如果能访问到全图, 则答案为其所在SCC的大小,否则为0. [代码] #include <cstdio> #include <algorithm> #include <vector> #include <cstring>

【学习整理】Tarjan:强连通分量+割点+割边

Tarjan求强连通分量 在一个有向图中,如果某两点间都有互相到达的路径,那么称中两个点强联通,如果任意两点都强联通,那么称这个图为强联通图:一个有向图的极大强联通子图称为强联通分量.   算法可以在 的时间内求出一个图的所有强联通分量. 表示进入结点 的时间 表示从 所能追溯到的栈中点的最早时间 如果某个点 已经在栈中则更新  否则对 进行回溯,并在回溯后更新  #include<iostream> #include<cstdlib> #include<cstdio>

【强连通分量】tarjan算法及kosaraju算法+例题

阅读前请确保自己知道强连通分量是什么,本文不做赘述. Tarjan算法 一.算法简介 Tarjan算法是一种由Robert Tarjan提出的求有向图强连通分量的时间复杂度为O(n)的算法. 首先我们要知道两个概念:时间戳(DFN),节点能追溯到的最早的栈中节点的时间戳(LOW).顾名思义,DFN就是在搜索中某一节点被遍历到的次序号(dfs_num),LOW就是某一节点在栈中能追溯到的最早的父亲节点的搜索次序号. Tarjan算法是基于深度优先搜索的算法.在搜索过程中把没有Tarjan过的点入栈