版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
引言
缩点,哲学的东西,你必须拥有。。。
缩点
个人认为就是把一堆强连通的点( 即强连通分量 ),认作为一个点
强连通分量就是这里面的点可以相互到达(算是个环)
详解
一个有向图如下
可以看出有强连通分量 { 1 , 2 } , { 8 , 4 , 9 } , { 7 } , { 6 } , { 3 } , { 5 }
先走一波Tarjan,求出强连通分量
用一个 cnt 记录一下强连通分量的和
然后在Trjan中记录每个点所属强连通分量,以及一些sao操作
void Tarjan( int x ) {//求强连通分量,缩点
num ++ ;
DFN[x] = low[x] = num ;
S.push(x);
inS[x] = 1 ;
for(int i = 0 ; i < G[x].size() ; ++ i ) {
int s = G[x][i] ;
if( !DFN[s] ) {
Tarjan( s );
low[x] = min( low[x] , low[s] );
}
else if( inS[s] )
low[x] = min( low[x] , DFN[s] );
}
if( low[x] == DFN[x] ) {
cnt ++ ;
int now ;
do{
now = S.top() ;
S.pop();
d[now] = cnt ;
inS[now] = 0 ;
if( now == sta )//记录缩点后的起始点
be = cnt ;
W[cnt] += w[now] ;
}while( now != x );
}
}
然后就是连接缩点后的图
先记录一下每条边
for( int i = 1 ; i <= m ; ++ i ) {
int a , b ;
scanf("%d%d", &a , &b );
G[a].push_back(b);
way[i].from = a , way[i].to = b ;
}
然后就是关键,一波sao操作
如果原图一条边上两点属于不同的强连通分量,则两个强连通分量相连,就可以对这两个分量连接
for( int i = 1 ; i <= m ; ++ i ) {//连接缩点后的图
int a = d[way[i].from] ;
int b = d[way[i].to] ;
if( a != b )//不属于同一分量
g[a].push_back(b);
}
然后可以得到缩点后的图,就可以为所欲为了...嘿嘿嘿......嘿
模板
又多一个板子背。。。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <vector>
#include <queue>
#include <stack>
#include <map>
#define ll long long
#define MAXN 500005
using namespace std ;
vector <int> G[MAXN] ;//初始图
vector <int> g[MAXN] ;//缩点后的图
stack <int> S ;
int m , n , ans ;
int sta ;
int w[MAXN] , d[MAXN] ;//w点权,d表示该点所属分量
bool inS[MAXN] ;//inS是否在栈中 ,vis在SPFA ,inQ是否在队列
int DFN[MAXN] , low[MAXN] ;//Tarjan用
int cnt , W[MAXN] , num , be , fsum , f[MAXN] ;//缩点信息
struct node{
int from , to ;
}way[MAXN];
void Tarjan( int x ) {//求强连通分量,缩点
num ++ ;
DFN[x] = low[x] = num ;
S.push(x);
inS[x] = 1 ;
for(int i = 0 ; i < G[x].size() ; ++ i ) {
int s = G[x][i] ;
if( !DFN[s] ) {
Tarjan( s );
low[x] = min( low[x] , low[s] );
}
else if( inS[s] )
low[x] = min( low[x] , DFN[s] );
}
if( low[x] == DFN[x] ) {
cnt ++ ;
int now ;
do{
now = S.top() ;
S.pop();
d[now] = cnt ;
inS[now] = 0 ;
if( now == sta )
be = cnt ;
W[cnt] += w[now] ;
}while( now != x );
}
}
int main() {
scanf("%d%d", &n , &m );//基本输入
for( int i = 1 ; i <= m ; ++ i ) {
int a , b ;
scanf("%d%d", &a , &b );
G[a].push_back(b);
way[i].from = a , way[i].to = b ;
}
for( int i = 1 ; i <= n ; ++ i )
scanf("%d", &w[i] );
scanf("%d%d", &sta );
for( int i = 1 ; i <= n ; ++ i ) {//缩点
num = 0 ;
if( !DFN[i] )
Tarjan( i );
}
for( int i = 1 ; i <= m ; ++ i ) {//连接缩点后的图
int a = d[way[i].from] ;
int b = d[way[i].to] ;
if( a != b )
g[a].push_back(b);
}
}