传送门
本题的标算是并查集查找连通块的个数,为了练习下Tarjan,就用Tranjan写一波;
思路:tarjan
观察题目,市政府“村村通工程”的目标是使全市任何两个城镇间都可以实现交通,表明图为多个联通块,因为是无向边,所以每个联通块一定是一个强连通分量(显然)。
那么问题就变成了 查找联通块,将所有联通块联通,显然需要n-1条边。
所以 只需要tarjan搜索强连通分量的个数,然后输出强连通分量个数减一即为答案。
代码如下:
#include <bits/stdc++.h> using namespace std; const int MAXN=100005; int n,m; vector<int> G[MAXN];//试一下动态存图 int dfn[MAXN],low[MAXN],cnt=0,index_=0; int scc[MAXN],size[MAXN]; bool inStack[MAXN]; stack<int> st; void tarjan(int u) { index_++; dfn[u]=index_; low[u]=index_; st.push(u); inStack[u]=true; for(int i=0;i<G[u].size();i++) { if(!dfn[G[u][i]]) { tarjan(G[u][i]); low[u]=min(low[u],low[G[u][i]]); } else if(inStack[G[u][i]]) low[u]=min(low[u],dfn[G[u][i]]); } if(low[u]==dfn[u]) { cnt++; while(st.top()!=u) { scc[st.top()]=cnt; size[cnt]++; inStack[st.top()]=false; st.pop(); } scc[st.top()]=cnt; size[cnt]++; inStack[st.top()]=false; st.pop(); } } inline void clear() { for(int i=0;i<=MAXN;i++) G[i].clear(); cnt=0; memset(scc,0,sizeof(scc)); memset(inStack,0,sizeof(inStack)); memset(size,0,sizeof(size)); memset(low,0,sizeof(low)); memset(dfn,0,sizeof(dfn)); while( !st.empty() ) st.pop(); index_=0; } int main() { ios::sync_with_stdio(0); //syn加速 for(;;) { clear(); cin>>n; if(n==0) break; cin>>m; for(int i=0;i<m;i++) { int x,y; cin>>x>>y; G[x].push_back(y); G[y].push_back(x); } for(int i=1;i<=n;i++) if(!dfn[i]) tarjan(i); cout<<cnt-1<<endl; } return 0; }