[题解]P3225 [HNOI2012] 矿场搭建
挖煤点坍塌相当于把该点和与其相连的边在图上删掉。
借用wjyyy的题解,我们定义“叶子连通块”为“只包含\(1\)个割点的点双连通分量”,“非叶子连通块”为“包含\(\ge 2\)个割点的点双连通分量”。
如下图,橙色点是割点,红色框圈出的是点双,加粗的是叶子连通块。
叶子连通块只有\(1\)个割点,所以必须保证该连通块内部存在逃生出口,否则割点塌陷,里面的人就逃不出去了。显然逃生出口只要设置在此连通块的非割点处即可。
由于非叶子连通块有\(\ge 2\)个割点,所以就算其中一个割点塌陷,该连通块的人仍然可以通过其他未塌陷的割点跑到叶子连通块或者其他连通块去。既然任何一个直接可达的叶子连通块都已经存在逃生出口了,那就不必额外耗费资源去建逃生出口了。
所以这道题第\(1\)问的答案是叶子连通块的个数,第\(2\)问的答案是(每个叶子连通块的大小\(-1\))的乘积。
注意特判不存在叶子连通块(也就是整张图不存在割点)的情况,此时需要建\(2\)个逃生出口,以防其中一个塌陷。答案是\(C_n^2=\frac{n(n-1)}{2}\)。
时间复杂度:每组数据\(O(n)\)。
点击查看代码
#include<bits/stdc++.h> #define int long long #define N 1010 using namespace std; int n,m,dfn[N],low[N],tim,cnt1,cnt2; stack<int> p; bitset<N> is,vis; vector<int> G[N]; void tarjan(int u){ dfn[u]=low[u]=++tim; int ch=0; for(int i:G[u]){ if(!dfn[i]){ tarjan(i),ch++; low[u]=min(low[u],low[i]); if(u!=1&&low[i]>=dfn[u]) is[u]=1; }else low[u]=min(low[u],dfn[i]); } if(u==1&&ch>=2) is[u]=1; } void dfs(int u){ if(vis[u]) return; vis[u]=1,cnt2++; if(is[u]){ p.push(u),cnt1++; return; } for(int i:G[u]) dfs(i); } void solve(int num){ tarjan(1); int ans1,ans2; if(is.none()){ ans1=2,ans2=n*(n-1)/2; }else{ ans1=0,ans2=1; for(int i=1;i<=n;i++){ if(is[i]) continue; cnt1=cnt2=0,dfs(i); while(!p.empty()) vis[p.top()]=0,p.pop(); if(cnt1==1) ans1++,ans2*=(cnt2-1); } } cout<<"Case "<<num<<": "<<ans1<<" "<<ans2<<"\n"; } signed main(){ for(int koishi=1;;koishi++){ memset(dfn,0,sizeof dfn); memset(low,0,sizeof low); vis=is=tim=n=0; cin>>m; if(!m) break; for(int i=1,u,v;i<=m;i++){ cin>>u>>v; G[u].emplace_back(v); G[v].emplace_back(u); n=max(n,max(u,v)); } solve(koishi); for(int i=1;i<=n;i++) G[i].clear(); } return 0; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
2023-11-13 [题解]P1536 村村通