「割点」&「割边」学习笔记
当遍历到一个点x时,这个点为割点的情况有两种:
- 第一种是该节点为根节点且子节点数>=2,必导致两个子节点不连通;
- 第二种是该节点不为根节点,且
,即子节点i可回溯到的最早的点不早于x点,那么删去x一定导致x的子节点不连通;
反之,若
//无向图割点模板 #include<bits/stdc++.h> #define int long long #define endl '\n' #define N 20001 using namespace std; template<typename Tp> inline void read(Tp&x) { x=0;register bool f=1; register char c=getchar(); for(;c<'0'||c>'9';c=getchar()) if(c=='-') f=0; for(;'0'<=c&&c<='9';c=getchar()) x=(x<<1)+(x<<3)+(c^48); x=(f?x:~x+1); } int n,m,a,b,tot,root,ans,dfn[N],low[N]; bool cut[N]; vector<int> e[N]; void tarjan(int x) { dfn[x]=low[x]=++tot; int child=0; for(int y:e[x]) if(!dfn[y]) { tarjan(y), low[x]=min(low[x],low[y]); if(low[y]>=dfn[x]) { child++; if(x!=root||child>1) cut[x]=1; } } else low[x]=min(low[x],dfn[y]); } signed main() { #ifndef ONLINE_JUDGE freopen("in.txt","r",stdin); freopen("out.txt","w",stdout); #endif read(n),read(m); while(m--) read(a),read(b), e[a].push_back(b), e[b].push_back(a); for(root=1;root<=n;root++) if(!dfn[root]) tarjan(root); for(int i=1;i<=n;i++) if(cut[i]) ans++; cout<<ans<<endl; for(int i=1;i<=n;i++) if(cut[i]) cout<<i<<' '; }
- 样例输入:
7 10
1 2
2 3
3 4
2 4
1 4
1 5
5 6
5 8
7 8
5 7 - 样例输出
2
1 5
无向图的割边判定法则
- 在遍历到一个点x 的子节点y,满足
,则 这条边就是割边; ,说明不经过 ,y无法到达比x更早的点,故割掉这条边;- 反之,若
,则y能够到达x或比x更早的点, 就不是割边,即环内没有割边;
-
求桥的时候,议案为边是无向的,所以父亲与孩子的关系也要自己规定一下,正常的
应改为 ;因为如果y是x的父亲,这条无向边就被误认为是环了; -
找桥的时候,有重边的一定不是桥;
割边是(1,2)和(1,5)。
板子:洛谷 P1656 炸铁路
- 题目描述
A国要使B国的铁路系统瘫痪,要炸毁一条铁路使其铁路无法全部联通,问可炸毁那一条铁路? - 输入格式
第一行 ( ),分别表示n个城市和m条铁路。
以下m行,每行 ,表示城市a和城市b间有铁路直接联通。 - 输出格式
输出若干行,每行 ,其中 ,表示 是 .
请注意:输出时,所有的数对必须按照a从小到大排序输出,若a相同,根据b从小到大输出。
代码实现:
//无向图割边模板,炸铁路 #include<bits/stdc++.h> #define int long long #define endl '\n' #define N 200 #define M 20001 using namespace std; template<typename Tp> inline void read(Tp&x) { x=0;register bool f=1; register char c=getchar(); for(;c<'0'||c>'9';c=getchar()) if(c=='-') f=0; for(;'0'<=c&&c<='9';c=getchar()) x=(x<<1)+(x<<3)+(c^48); x=(f?x:~x+1); } int n,m,a,b,tot,ans,dfn[N],low[N]; bool v[N][N],again[N][N]; vector<int> e[M]; struct aa{int l,r;}s[M]; bool cmp(aa s1,aa s2){if(s1.l==s2.l) return s1.r<s2.r;return s1.l<s2.l;} void tarjan(int x,int fa) { dfn[x]=low[x]=++tot; for(int y:e[x]) if(!dfn[y]) { tarjan(y,x), low[x]=min(low[x],low[y]); if(low[y]>dfn[x]) s[++ans]=(aa){x,y}; } else if(y!=fa||again[x][y]) low[x]=min(low[x],dfn[y]);//重边一定不是割边 } signed main() { #ifndef ONLINE_JUDGE freopen("in.txt","r",stdin); freopen("out.txt","w",stdout); #endif read(n),read(m); while(m--) { read(a),read(b); if(v[a][b]) { again[a][b]=again[b][a]=1;//判断重边 continue; } v[a][b]=v[b][a]=1, e[a].push_back(b), e[b].push_back(a); } for(int i=1;i<=n;i++) if(!dfn[i]) tarjan(i,i); stable_sort(s+1,s+1+ans,cmp); for(int i=1;i<=ans;i++) cout<<s[i].l<<' '<<s[i].r<<endl; }
- 样例输入
7 10
1 2
2 3
2 4
3 4
1 5
5 6
5 7
7 8
5 8
6 5 - 样例输出
1 2
1 5
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效