强连通,Tarjan,缩点
在本文中,我们用 \(f(x,y)=1\) 来表示 \(x\) 可以到达点 \(y\),用 \(g(x,y)=1\) 表示 \(f(x,y)=1\) 且 \(f(y,x)=1\)。
I、强连通
对于图 \(U\) 上的任意两点 \(x\) 和 \(y\),如果有 \(g(x,y)=1\),那么称 \(x,y\) 是强连通的。
对于图 \(U\) 的子图 \(V\),若 \(V\) 中任意两点 \(x,y\) 都满足 \(g(x,y)=1\),则称图 \(V\) 是一个强连通分量。
II、Tarjan
一、定义
vector <int> v[N]表示原图。int dfn[N]表示 dfs 序中第 \(i\) 个点的时间戳。int low[N]表示节点 \(i\) 所处的强连通分量中最小的时间戳。stack <int> st表示 dfs 的路径上的节点。bool is[N]表示节点 \(i\) 是否处于栈中。int tim表示时间戳。
二、具体操作
- 找到一个未被访问过的节点 \(x\),将 \(x\) 入栈,更新 \(x\) 的时间戳,且更新:
vis[x] = 1;
low[x] = dfn[x];
is[x] = 1;
st.push(x);
- 遍历所有邻接节点 \(y\):
-
若 \(y\) 未被访问,则继续递归,并更新
low[x] = min(low[x],low[y]);。 -
若 \(y\) 被访问过,且在栈中,则更新
low[x] = min(low[x],dfn[y]); -
若 \(y\) 被访问过,但不在栈中,说明 \(y\) 在另一个强连通分量中。
- 遍历完成后,如果
low[x] = dfn[x],说明 \(x\) 是一个强连通分量中编号最小的,从栈中弹出所有和 \(x\) 在一个强连通分量里的元素 \(y\),并更新:
low[y] = low[x];
由于我们是按路径遍历的,所以栈剩下的一定不和 \(x\) 在一个强连通分量里。
时间复杂度为 O(n)。
III.缩点
对于每一个强连通分量,它的内部都是联通的,如果内部的点之间没有贡献或贡献容易计算,那么我们可以把它当成一个点来看。
一、定义
只需要在 Tarjan 求强连通分量的情况下,添加:
int num[N]第 \(i\) 个点的缩点后的编号。int sum编号的计数戳。
二、具体步骤
在 Tarjan 求强连通分量的情况下,添加:
- 对于 \(x\),更新:
sum++,num[x] = sum;
- 在遍历完成后弹出的元素 \(y\) 更新:
num[y] = num[x];
三、更多的操作:
可以添加:
int siz[N]表示第 \(i\) 个强连通分量(或点)的大小。int in[N]表示第 \(i\) 个点的入度。int out[N]表示第 \(i\) 个点的出度。
IIII.例题
- 给你一个 \(n\) 个点,\(m\) 条边的有向图,如果第 \(i\) 个点的颜色为 1,那么它相邻的节点的颜色也为 1,问最少将多少个节点的颜色染成 1,才能使最后所有节点颜色为 1。
分析:对于每一个缩点,只要一个节点颜色为 1,那么整个缩点的颜色为 1,缩点的颜色还可以往相邻的缩点传,最终就是要求缩点后有多少个连通块。
// \\\\ \\ \\ \\ \\ \\ \\ =88888= // // // // // // ////
// \\\\ \\ \\ \\ \\ \\ o8888888o // // // // // ////
// \\\\ \\ \\ \\ \\ 88" . "88 // // // // ////
// \\\\ \\ \\ \\ (| -_- |) // // // ////
// \\\\ \\ \\ O\ = /O // // ////
// \\\\ \\ ____/`---'\____ // ////
// \\\\ .' \\| |// `. ////
// //== / \\||| : |||// \ ==\\
// //== / _||||| -:- |||||- \ ==\\
// //== | | \\\ - /// | | ==\\
// //== | \_| ''\---/'' | | ==\\
// //== \ .-\__ `-` ___/-. / ==\\
// //== ___`. .' /--.--\ `. . ___ ==\\
// //== ."" '< `.___\_<|>_/___.' >' "". ==\\
// //== | | : `- \`.;`\ _ /`;.`/ - ` : | | \\\\
// //// \ \ `-. \_ __\ /__ _/ .-` / / \\\\
// //// ========`-.____`-.___\_____/___.-`____.-'======== \\\\
// //// `=---=' \\\\
// //// // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ \\ \\\\
// //// // // 佛祖保佑 永无BUG 永不修改 \\ \\ \\\\\\
// //// // // // // // || || || || || || || || || || \\ \\ \\ \\ \\ \\\\
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<ll, ll> P;
typedef __int128 int128;
#define IOS ios::sync_with_stdio(0);
#define rep(i,x,y,z) for(int i = x;i <= y;i+=z)
#define per(i,y,x,z) for(int i = y;i >= x;i-=z)
#define frin(x) freopen(x,"r",stdin);
#define frout(x) freopen(x,"w",stdout);
const int N = 1e6 + 5;
const int M = 2e3 + 5;
const int K = 18;
const double eps = 1e-6;
const int INF = 0x3f3f3f3f;
const int dx[] = {-1, 1, 0, 0, -1, 1, -1, 1};
const int dy[] = {0, 0, -1, 1, 1, -1, -1, 1};
namespace better_function {
inline ll read() {
ll s = 0, w = 1;
char c = getchar();
while (c < '0' || c > '9') {
if (c == '-') w = -1;
c = getchar();
}
while (c >= '0' && c <= '9') {
s = s * 10 + c - '0';
c = getchar();
}
return s * w;
}
inline void print(ll x) {
if (x < 0) {
putchar('-');
x = -x;
}
if (x >= 10) {
print(x / 10);
}
putchar(x % 10 + '0');
return;
}
inline ll pow(ll x, ll y, ll z) {
if (z == 0) z = INF;
ll ans = 1;
while (y) {
if (y % 2) {
ans *= x;
}
x *= x;
y >>= 1;
}
return ans;
}
inline ll gcd(ll a, ll b) {
return b == 0 ? a : gcd(b, a % b);
}
inline ll lcm(ll a, ll b) {
return a * b / gcd(a, b);
}
}
namespace ex_prime {
bool Isprime[N];
int Phi[N], Prime[N], prime_num;
inline void euler(ll n) {
Phi[1] = 1;
rep(i, 2, n, 1) {
if (Isprime[i] == 0) Prime[++prime_num] = i, Phi[i] = i - 1;
rep(j, 1, prime_num, 1) {
if (Prime[j] * i > n) break;
Isprime[i * Prime[j]] = 1;
if (i % Prime[j] == 0) {
Phi[i * Prime[j]] = Phi[i] * Prime[j];
break;
} else Phi[i * Prime[j]] = Phi[i] * Phi[Prime[j]];
}
}
}
inline int prime(ll x) {
if (!prime_num) euler(N);
if (x > prime_num) return -1;
return Prime[x];
}
inline int phi(ll x) {
if (!prime_num) euler(N);
if (x > N - 5) return -1;
return Phi[x];
}
inline bool isprime(ll x) {
if (!prime_num) euler(N);
if (x > N - 5) return -1;
return Isprime[x];
}
}
inline void solve();
int main() {
IOS;
//frin("");frout("");
int T = 1;
//cin >> T;
while (T--) {
solve();
}
return 0;
}
using namespace better_function;
using namespace ex_prime;
int n,dfn[N],low[N],tim;
bool is[N];
int sum,col[N],in[N];
stack <int> st;
vector <int> v[N];
void dfs_tarjan(int x) {
dfn[x] = low[x] = ++tim;
st.push(x);
is[x] = 1;
rep(i,0,(int)v[x].size() - 1,1) {
int y = v[x][i];
if(!dfn[y]) {
dfs_tarjan(y);
low[x] = min(low[x],low[y]);
}
else if(is[y]) {
low[x] = min(low[x],dfn[y]);
}
}
if(dfn[x] == low[x]) {
int y;
sum++;
do{
y = st.top();
st.pop();
col[y] = sum;
is[y] = 0;
}while(y != x);
}
}
void Tarjan() {
rep(i,1,n,1) {
if(!dfn[i]) {
dfs_tarjan(i);
}
}
}
inline void solve() {
cin >> n;
rep(i,1,n,1) {
rep(j,1,n,1) {
int x;
cin >> x;
if(x) v[i].push_back(j);
}
}
Tarjan();
rep(i,1,n,1) {
rep(j,0,(int)v[i].size() - 1,1) {
int y = v[i][j];
// cout << i << ' ' << col[i] << ' ' << y << ' ' << col[y] << endl;
if(col[i] != col[y]) {
in[col[y]]++;
}
}
}
int ans = 0;
rep(i,1,sum,1) {
if(!in[i]) ans++;
}
cout << ans;
}

浙公网安备 33010602011771号