D16 割点 Tarjan 算法
D16 割点 Tarjan 算法_哔哩哔哩_bilibili
有向图:强连通分量、缩点
无向图:割点、割边、点双连通分量、边双连通分量、缩点
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"; }
浙公网安备 33010602011771号