强联通分量


强连通分量

如果两个顶点可以相互通达,则称两个顶点强连通(strongly connected)。如果有向图G的每两个顶点都强连通,称 G 是一个强连通图。有向图的极大强连通子图,称为强连通分量(strongly connected components)。

常用于统计环的数量,割点割边。

P3388 【模板】割点(割顶)

割点(割顶)

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<queue>
using namespace std;
int r_d(){
  int x=0,f=1;
  char c=getchar();
  while(!isdigit(c)||c=='-'){
    if(c=='-')f=-1;
    c=getchar();
  }
  while(c>='0'&&c<='9'){
    x=(x<<3)+(x<<1)+(c^48);
    c=getchar();
  }
  return x*f;
}
struct po{
  int v;
  int n_t;
}p_p[1000001];
int h_d[1000001],x_h;
void a_d(int u,int v){//存边
  p_p[++x_h].v=v;
  p_p[x_h].n_t=h_d[u];
  h_d[u]=x_h;
}
int d_f[1000001],l_w[1000001],x_x;//d_f 时间戳 l_w 可以到达最小的时间戳(判环)
int c_c;
int s[1000001],top,b_l[1000001];
int g_d[1000001],r_r=0;
void t_j(int k){
  d_f[k]=l_w[k]=++x_x;
  int s_t=0;
  for(int i=h_d[k];i;i=p_p[i].n_t){
    int v=p_p[i].v;
    if(!d_f[v]){
      t_j(v);
      l_w[k]=min(l_w[k],l_w[v]);
      if(l_w[v]>=d_f[k]){//判断割点
        s_t++;
        if(s_t>=2||r_r!=k)g_d[k]=1;
      }
    }else {
      l_w[k]=min(l_w[k],d_f[v]);
    }
  }
}
int a_s,n,m;
int main(){
  n=r_d();m=r_d();
  int u,v;
  for(int i=0;i<m;i++){
    u=r_d();v=r_d();
    if(u==v)continue;
    a_d(u,v);a_d(v,u);
  }
  for(int i=1;i<=n;i++)if(!d_f[i])r_r=i,t_j(i);//判断是否遍历过
  for(int i=1;i<=n;i++)if(g_d[i])a_s++;
  cout<<a_s<<endl;
  for(int i=1;i<=n;i++)if(g_d[i])cout<<i<<" ";
  cout<<endl;
  return 0;
}

P1656 炸铁路

割边

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<vector>
#include<queue>
using namespace std;
int r_r(){
  int x=0,f=1;
  char c=getchar();
  while(!isdigit(c)){
    if(c=='-')f=-1;
    c=getchar();
  }
  while(isdigit(c)){
    x=(x<<1)+(x<<3)+(c^48);
    c=getchar();
  }
  return x*f;
}
const int o_o=2e5+10;
int n,m;
struct po{
  int v;
  int n_t;
}p_p[o_o];
int h_d[o_o],x_p;
void a_d(int u,int v){//存边
  p_p[++x_p].v=v;
  p_p[x_p].n_t=h_d[u];
  h_d[u]=x_p;
}
int l_w[o_o],d_f[o_o],x_x,f_f[o_o];
struct pp{
  int l,r;
}a_s[o_o];
int x_a;
bool cmp(pp a,pp b){
  return a.l==b.l?a.r<b.r:a.l<b.l;
}
void t_j(int k){
  d_f[k]=l_w[k]=++x_x;
  for(int i=h_d[k];i;i=p_p[i].n_t){
    int v=p_p[i].v;
    if(d_f[v]&&v!=f_f[k])l_w[k]=min(l_w[k],d_f[v]);//若果是祖先,割边就没用了
    if(!d_f[v]){
      f_f[v]=k;//建立祖先关系
      t_j(v);
      l_w[k]=min(l_w[v],l_w[k]);
      if(l_w[v]>d_f[k])a_s[++x_a]={k,v};//割边判断条件
    }
  }
}
int main(){
  n=r_r();m=r_r();
  for(int i=1;i<=m;i++){
    int a=r_r(),b=r_r();
    a_d(a,b);
    a_d(b,a);
  }
  for(int i=1;i<=n;i++)if(!d_f[i])t_j(i);//每个店都要遍历到,有时间戳
  sort(a_s+1,a_s+1+x_a,cmp);//根据题目要求排序
  for(int i=1;i<=x_a;i++)cout<<a_s[i].l<<" "<<a_s[i].r<<endl;
  return 0;
}

P3387 【模板】缩点

缩点

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<vector>
#include<queue>
using namespace std;
const int o_o=1e5+10;
int r_r(){
  int x=0,f=1;
  char c=getchar();
  while(!isdigit(c)){
    if(c=='-')f=-1;
    c=getchar();
  }
  while(isdigit(c)){
    x=(x<<1)+(x<<3)+(c^48);
    c=getchar();
  }
  return x*f;
}
int n,m;
int p_t[o_o];
struct po{
  int v;
  int n_t;
}p_p[o_o];
int h_d[o_o],x_p,i_a[o_o],i_b[o_o];
void a_d(int u,int v){
  p_p[++x_p].v=v;
  p_p[x_p].n_t=h_d[u];
  h_d[u]=x_p;
}
int d_f[o_o],l_w[o_o],x_x,c_c,t_p,s_s[o_o],b_b[o_o];
int s_m[o_o],n_w[o_o];
void t_j(int k){
  d_f[k]=l_w[k]=++x_x;//初始化时间戳
  s_s[++t_p]=k;//存环中的点
  b_b[k]=1;//是否遍历过
  for(int i=h_d[k];i;i=p_p[i].n_t){
    int v=p_p[i].v;
    if(!d_f[v]){
      t_j(v);
      l_w[k]=min(l_w[k],l_w[v]);
    }else if(b_b[v])l_w[k]=min(l_w[k],l_w[v]);//在同一环中
  }
  if(l_w[k]==d_f[k]){
    ++c_c;//新节点下标
    while(s_s[t_p+1]!=k){//判断是否遍历完这个环
      n_w[s_s[t_p]]=c_c;//赋新下标
      s_m[c_c]+=p_t[s_s[t_p]];//存环节点的值的和(根据题目要求)
      b_b[s_s[t_p--]]=0;
    }
  }
}
int f_f[o_o];
void f_i(int k){
  if(f_f[k])return;
  f_f[k]=s_m[k];
  int m_a=0;
  for(int i=h_d[k];i;i=p_p[i].n_t){//找最大值
    int v=p_p[i].v;
    if(!f_f[v])f_i(v);
    m_a=max(m_a,f_f[v]);//更新
  }
  f_f[k]+=m_a;//累加答案
}
int a_s;
int main(){ 
  n=r_r(),m=r_r();
  for(int i=1;i<=n;i++)p_t[i]=r_r();
  for(int i=1;i<=m;i++){//存初始边
    i_a[i]=r_r();
    i_b[i]=r_r();
    a_d(i_a[i],i_b[i]);
  }
  for(int i=1;i<=n;i++)if(!d_f[i])t_j(i);
  memset(p_p,0,sizeof p_p);//清空从新存新边
  memset(h_d,0,sizeof h_d);
  x_p=0;
  for(int i=1;i<=m;i++)//不属于同一个环的连边(新点相当于原来的一个环)
    if(n_w[i_a[i]]!=n_w[i_b[i]])a_d(n_w[i_a[i]],n_w[i_b[i]]);
  for(int i=1;i<=c_c;i++){//便利每个新下标
    if(!f_f[i]){
      f_i(i);
      a_s=max(f_f[i],a_s);//更新更优解
    }
  }
  cout<<a_s<<endl;
  return 0;
} 

文章作者: 王大神——A001
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 王大神——A001 !
  目录