D16 割点 Tarjan 算法

D16 割点 Tarjan 算法_哔哩哔哩_bilibili

P3388 【模板】割点(割顶) - 洛谷

有向图:强连通分量、缩点

无向图:割点、割边、点双连通分量、边双连通分量、缩点

1. 割点:删掉后使图从连通变成不连通的点

2. 判儿子:low[y]>=dfn[x](说明 y 没有连通 x 之前的点)

3. 判割点:x!=root || son>1(① 若 x 不是根且 son 至少 1 个; ② 若 x 是根且 son 至少 2 个)

4. 若y已访问:必须用 dfn[y] 更新 low[x]。因为边是双向的,如果 low[y]=low[x],那么 low[y]<dfn[x],无法判割点。

#include<bits/stdc++.h>
using namespace std;

const int N=20010;
int n,m,a,b;
vector<int> e[N];
int dfn[N],low[N],tim,cut[N],root;

void tarjan(int x){
  dfn[x]=low[x]=++tim;
  int son=0; //x的儿子个数
  for(int y:e[x]){
    if(!dfn[y]){ //若y未访问
      tarjan(y);
      low[x]=min(low[x],low[y]); 
      if(low[y]>=dfn[x]){
        son++;
        if(x!=root||son>1) cut[x]=1;
      }
    }
    else //若y已访问
      low[x]=min(low[x],dfn[y]); //注:dfn不能换成low
  }
}
int main(){
  cin>>n>>m;
  while(m --){
    cin>>a>>b;
    e[a].push_back(b),
    e[b].push_back(a);
  }
  for(root=1;root<=n;root++) if(!dfn[root]) tarjan(root);
  
  int ans=0;
  for(int i=1;i<=n;i++) if(cut[i]) ans++;
  cout<<ans<<"\n";
  for(int i=1;i<=n;i++) if(cut[i]) cout<<i<<" ";
}

 

P3469 [POI 2008] BLO-Blockade - 洛谷

#include<bits/stdc++.h>
using namespace std;

#define int long long
const int N=100010;
int n,m,a,b;
vector<int> e[N];
int dfn[N],low[N],tim,cut[N],siz[N],ans[N];

void tarjan(int x){
  dfn[x]=low[x]=++tim; siz[x]=1;
  int son=0,sum=0;
  for(int y:e[x]){
    if(!dfn[y]){ //若y未访问
      tarjan(y);
      low[x]=min(low[x],low[y]);
      siz[x]+=siz[y];
      if(low[y]>=dfn[x]){
        // son++;
        // if(x!=1||son>1) cut[x]=1;
        ans[x]+=siz[y]*(n-siz[y]); //y子树的贡献
        sum+=siz[y];
      }
    }
    else low[x]=min(low[x],dfn[y]);
  }
  ans[x]+=(n-sum-1)*(sum+1); //x子树外的贡献
  ans[x]+=n-1; //x的贡献
}
signed main(){
  ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
  cin>>n>>m;
  while(m --){
    cin>>a>>b;
    e[a].push_back(b),
    e[b].push_back(a);
  }
  tarjan(1);
  for(int i=1;i<=n;i++) cout<<ans[i]<<"\n";
}

 

割点和桥 - OI Wiki

 

posted @ 2022-05-28 13:30  董晓  阅读(1570)  评论(0)    收藏  举报