牛客小白月赛12 392I

链接:https://ac.nowcoder.com/acm/contest/392/I
来源:牛客网

时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 32768K,其他语言65536K
64bit IO Format: %lld

题目描述

月月和华华一起去逛公园了。公园很大,为了方便,可以抽象的看成一个N个点M条边的无向连通图(点是景点,边是道路)。公园唯一的入口在1号点,月月和华华要从这里出发,并打算参观所有的景点。因为他们感情很好,走多远都不会觉得无聊,所以所有景点和道路都可以无数次的重复经过。月月发现,有些路可走可不走,有些路则必须要走,否则就无法参观所有的景点。现在月月想知道,有几条路是不一定要经过的。因为这是个很正常的公园,所以没有重边和自环。

输入描述:

第一行两个正整数N和M,表示点数和边数。
接下来M行,每行两个正整数U和V表示一条无向边。
保证给定的图是连通的。

输出描述:

输出一行一个非负整数表示不一定要经过的边有几条。
示例1

输入

复制
5 5
1 2
2 3
3 4
4 5
3 5

输出

复制
3

说明

例如第三条边,月月和华华可以依次走过第一条、第二条、第五条、第四条边走过全部的景点,所以第三条边不一定要经过。同理还有第四条、第五条边,答案为3。

备注:

1N1051≤N≤105,1M3×105


分析 : 求割边(桥)的模板题,注意因为是无向图,所以存边会加倍,应该开2*M大小的边的数组。

#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;
}
View Code

猜你喜欢

转载自www.cnblogs.com/paulzjt/p/10544377.html