一个公园中有 n 个景点,景点之间通过无向的道路来连接(明显的点双
),如果至少两个环公用一条路,路上的游客就会发生冲突;如果一条路不属于任何的环,这条路就没必要修
问,有多少路不必修,有多少路会发生冲突
每一个连通块中,如果边数大于点数,这个块中所有的边全部是冲突边。
所有桥为不需要修建的路。
点双连通分量:对于一个连通图,如果任意两点至少存在两条“点不重复”的路径,则说这个图是点双连通的(一般简称双连通),简单来说就是任意两条边都在同一个简单环中,即内部无割顶。
多余边:不在任何环中,一定是桥。
冲突边:如果一个环内的边数大于点数,那么这个环内所有边都是“冲突边”。
#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 ",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 ",cut,ans ); } return 0; }