双连通分量
题意:给一个无向图,问要添加多少条边形成边双连通分量。注意图一开始是连通的,所以只要从一个点开始dfs一次就行了,另外这图有重边,(1,2)(2,1)这样,则1,2就形成了一个边双连通分量。
之前写的求边双连通分量的代码不能处理重边,但是要修改过来其实挺简单的。重边无非是遇到一个问题,从u走到v,按一般的做法,是不能从v回到u的,即不能马上就回到它父亲节点去(其实指的是不能重复走这条边,这条边虽然是无向边但是只能走一次),但是有了重边后,是可以马上回到它父亲处的,只不过走的是另一条边。所以我们可以标记哪些边用过了,每条边只能用一次,用过一次后不能再用。而且别忘了,建图的时候,无向边是分成两条有向边来保存的,所以当一条有向边被使用后,它对应的另一条反边也不能使用了,要一同标记(这样才起到了每条无向边只能走一次的效果),标记的方法很常用,e[k].used = e[k^1].used = 1; 神奇的位运算
然后这个方格其实是个更好的方法,另外这样的代码,是能够同时处理有重边和无重边的情况的,所以就这样运行一下tarjan就能出结果
#include <iostream> #include <vector> #include <utility> #include <stack> #include <cstdio> #include <cstring> using namespace std; #define N 5010 #define M 10010 int n,tot; int head[N],dfn[N],low[N],belong[N],ins[N],de[N],dcnt,bcnt; typedef pair<int,int>pii; struct edge { int u,v,used,next; }e[2*M]; vector<pii>bridge; stack<int>sta; void add(int u , int v , int k) { e[k].u = u; e[k].v = v; e[k].used = 0; e[k].next = head[u]; head[u] = k++; u = u^v; v = u^v; u = u^v; e[k].u = u; e[k].v = v; e[k].used = 0; e[k].next = head[u]; head[u] = k++; } void dfs(int u ,int fa) { dfn[u] = low[u] = ++dcnt; sta.push(u); ins[u] = 1; for(int k=head[u]; k!=-1; k=e[k].next) if(!e[k].used) { e[k].used = e[k^1].used = 1; int v = e[k].v; if(!dfn[v]) //树边 { dfs(v , u); low[u] = min(low[u] , low[v]); if(low[v] > dfn[u]) //桥 { bridge.push_back(make_pair(u,v)); ++bcnt; while(true) { int x = sta.top(); sta.pop(); ins[x] = false; belong[x] = bcnt; if(x == v) break; } } } else if(ins[v]) //后向边 low[u] = min(low[u] , dfn[v]); } } void solve() { bridge.clear(); while(!sta.empty()) sta.pop(); memset(ins,0,sizeof(ins)); memset(dfn,0,sizeof(dfn)); memset(de,0,sizeof(de)); dcnt = bcnt = 0; dfs(1,-1); ++bcnt; while(!sta.empty()) { int x = sta.top(); sta.pop(); ins[x] = 0; belong[x] = bcnt; } // for(int i=1; i<=n; i++) // cout << i << "[" << low[i] << "]" << endl; for(int i=0; i<bridge.size(); i++) { int u = bridge[i].first; int v = bridge[i].second; de[belong[u]]++; de[belong[v]]++; } int leaf = 0; for(int i=1; i<=bcnt; i++) if(de[i] == 1) leaf++; cout << (leaf+1)/2 << endl; } int main() { while(cin >> n >> tot) { tot *= 2; memset(head,-1,sizeof(head)); for(int i=0; i<tot; i+=2) { int u,v; cin >> u >> v; add(u,v,i); } solve(); } return 0; }