一個公園中有 n 個景點,景點之間經過無向的道路來鏈接(明顯的點雙ios
),若是至少兩個環公用一條路,路上的遊客就會發生衝突;若是一條路不屬於任何的環,這條路就不必修ide
問,有多少路沒必要修,有多少路會發生衝突spa
每個連通塊中,若是邊數大於點數,這個塊中全部的邊所有是衝突邊。string
全部橋爲不須要修建的路。it
點雙連通份量:對於一個連通圖,若是任意兩點至少存在兩條「點不重複」的路徑,則說這個圖是點雙連通的(通常簡稱雙連通),簡單來講就是任意兩條邊都在同一個簡單環中,即內部無割頂。
多餘邊:不在任何環中,必定是橋。io
衝突邊:若是一個環內的邊數大於點數,那麼這個環內全部邊都是「衝突邊」。
class
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <set> #include <stack> using namespace std; typedef long long ll; #define cls(s,h) memset(s,h,sizeof s) const int maxn = 1e5 + 7; int n , m ; int tot; struct edge { int to,from,nxt; }e[maxn << 1]; int head[maxn]; void add_edge(int u , int v ){ e[tot].from = u ; e[tot].to = v; e[tot].nxt = head[u]; head[u] = tot++; } int dfn[maxn],low[maxn],idx; stack<edge> stk; set<int> bcc; int cut; //brige int ans; //the outway brige int flag; void tanjan(int u , int pre){ dfn[u] = low[u] = ++idx; for(int i = head[u]; ~i;i = e[i].nxt){ int v = e[i].to; if(v == pre) continue; if(!dfn[v]){ stk.push(e[i]); tanjan(v,u); low[u] = min(low[u],low[v]); if(low[v] >= dfn[u]){//割點 edge tmp; int cnt = 0; bcc.clear(); //bcc[++flag].push(e[i]); do{//找v.DCC的子集 cnt++;//子集的邊數 tmp = stk.top(); stk.pop(); //點數 bcc.insert(tmp.from); bcc.insert(tmp.to); }while(tmp.from != u || tmp.to != v); if(cnt > bcc.size()) ans += cnt; // flag ++; } if(low[v] > dfn[u]) ++cut; }else if(dfn[v] < dfn[u]){ stk.push(e[i]); low[u] = min(low[u],dfn[v]); } } } void init(){ cls(head,-1); cls(dfn,0); flag = ans = cut = tot = idx = 0; } int main(int argc, char const *argv[]) { while(scanf("%d %d",&n,&m) && n + m){ int u , v; init(); for(int i = 1;i <= m ;i ++){ scanf("%d %d",&u,&v); add_edge(u,v); add_edge(v,u); } for(int i = 1;i <= n;i ++){ if(!dfn[i]) tanjan(i,-1); } printf("%d %d\n",cut,ans ); } return 0; }
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <set> #include <stack> using namespace std; typedef long long ll; #define cls(s,h) memset(s,h,sizeof s) const int maxn = 1e5 + 7; int n , m ; int tot; struct edge { int to,from,nxt; }e[maxn << 1]; int head[maxn]; void add_edge(int u , int v ){ e[tot].from = u ; e[tot].to = v; e[tot].nxt = head[u]; head[u] = tot++; } int dfn[maxn],low[maxn],idx; stack<edge> stk; set<int> bcc; int cut; //brige 橋 int ans; //the outway brige 衝突的邊 int flag; void tanjan(int u , int pre){ dfn[u] = low[u] = ++idx; for(int i = head[u]; ~i;i = e[i].nxt){ int v = e[i].to; if(v == pre) continue; if(!dfn[v]){ stk.push(e[i]); tanjan(v,u); low[u] = min(low[u],low[v]); if(low[v] >= dfn[u]){//割點 割點斷定法則 edge tmp; int cnt = 0; bcc.clear(); //bcc[++flag].push(e[i]); do{//找v.DCC的子集 cnt++;//子集的邊數 tmp = stk.top(); stk.pop(); //點數 bcc.insert(tmp.from); bcc.insert(tmp.to); }while(tmp.from != u || tmp.to != v); if(cnt > bcc.size()) ans += cnt; // flag ++; } if(low[v] > dfn[u]) ++cut; }else if(dfn[v] < dfn[u]){ stk.push(e[i]); low[u] = min(low[u],dfn[v]); } } } void init(){ cls(head,-1); cls(dfn,0); flag = ans = cut = tot = idx = 0; } int main(int argc, char const *argv[]) { while(scanf("%d %d",&n,&m) && n + m){ int u , v; init(); for(int i = 1;i <= m ;i ++){ scanf("%d %d",&u,&v); add_edge(u,v); add_edge(v,u); } for(int i = 1;i <= n;i ++){ if(!dfn[i]) tanjan(i,-1); } printf("%d %d\n",cut,ans ); } return 0; }