链接:https://ac.nowcoder.com/acm/contest/392/I
来源:牛客网
时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 32768K,其他语言65536K
64bit IO Format: %lld
空间限制:C/C++ 32768K,其他语言65536K
64bit IO Format: %lld
题目描述
月月和华华一起去逛公园了。公园很大,为了方便,可以抽象的看成一个N个点M条边的无向连通图(点是景点,边是道路)。公园唯一的入口在1号点,月月和华华要从这里出发,并打算参观所有的景点。因为他们感情很好,走多远都不会觉得无聊,所以所有景点和道路都可以无数次的重复经过。月月发现,有些路可走可不走,有些路则必须要走,否则就无法参观所有的景点。现在月月想知道,有几条路是不一定要经过的。因为这是个很正常的公园,所以没有重边和自环。
输入描述:
第一行两个正整数N和M,表示点数和边数。
接下来M行,每行两个正整数U和V表示一条无向边。
保证给定的图是连通的。
输出描述:
输出一行一个非负整数表示不一定要经过的边有几条。
示例1
备注:
1≤N≤1051≤N≤105,1≤M≤3×105
分析 : 求割边(桥)的模板题,注意因为是无向图,所以存边会加倍,应该开2*M大小的边的数组。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <iostream> #include <cstring> #include <cstdio> #include <string> #include <queue> #include <list> #include <map> #include <set> #include <cmath> #include <bitset> #include <vector> #include <iomanip> #include <sstream> #include <cstdlib> #include <algorithm> using namespace std; typedef long long ll; #define mem(A, X) memset(A, X, sizeof A) #define foreach(e,x) for(__typeof(x.begin()) e=x.begin();e!=x.end();++e) #define fori(i,l,u) for(ll (i)=(ll)(l);(i)<=(ll)(u);++(i)) #define ford(i,l,u) for(ll (i)=(ll)(l);(i)>=(ll)(u);--(i)) //--------------------------s const int MAXN = 100010; const int MAXM = 600010; vector<int> bridges; vector<int> cuts; int head[MAXN], tot; struct Edge { int u,v, next; bool bridge; Edge(){} Edge(int pu,int pv,int pnext,bool p_bridge){u=pu; v=pv; next=pnext; bridge=p_bridge;} } edge[MAXM]; int Low[MAXN], DFN[MAXN]; bool is_cut[MAXN]; void init(){ tot=0; bridges.clear(); cuts.clear(); memset(is_cut,false,sizeof(is_cut)); memset(head,-1,sizeof(head)); memset(DFN,0,sizeof(DFN)); } void addedge(int u, int v) { // 只添加有向边u->v,无向图双向。 节点标号从0从1开始都可以 Edge &t=edge[tot]; t=Edge(u,v,head[u],false); head[u] = tot++; } void cut_bridge(int u, int father,int dep) { //节点可以标号任意,调用时从任一点调用即可 (1,-1,1) Low[u] = DFN[u] = dep; int son = 0; for (int i = head[u]; i != -1; i = edge[i].next) { int v = edge[i].v; if (v == father) continue; if (!DFN[v]) { son++; cut_bridge(v, u, dep+1); Low[u] = min(Low[u],Low[v]); if (Low[v] > DFN[u]) { //cout<<"bridges: "<<i<<" "<< (i^1) <<endl; edge[i].bridge=true; edge[i^1].bridge=true; } //桥 此处为无向图,故双向。 if (-1 != father && Low[v] >= DFN[u]) is_cut[u]=true; //割点 不是树根时 } else { Low[u] =min(Low[u], DFN[v]); } } if (-1 == father && son > 1) is_cut[u]=true;//割点 是树根时 } //---------------------------------------------------e //bug记录: //多组输入时每次 init(). int n,m; int main() { ios::sync_with_stdio(false); //freopen("local.in","r",stdin); while(cin>>n>>m){ //cout<<"n,m"<<n<<" "<<m<<endl; init(); fori(i,0,m-1){ int u,v; cin>>u>>v; addedge(u,v); addedge(v,u); } cut_bridge(1,-1,1); int cnt=0; fori(i,0,tot-1){ if(edge[i].bridge) cnt++; } //cout<<"cnt: "<<cnt<<endl; cout<<m-cnt/2<<endl; } return 0; }