問題
http://hihocoder.com/problemset/problem/1190?sid=787105
求點的連通分量
解法
使用tarjan,用堆棧記錄邊,在割點處彈棧,注意邊可能有兩次進入堆棧,我們記錄邊是否已經進入堆棧。
#include <bits/stdc++.h>
using namespace std;
enum{maxn = 20000+5, maxm = 100000+5};
bool visitV[maxn];
bool visitE[maxm];
int eGroup[maxm];
struct E{
int b;
int edgeId;
};
vector<E> G[maxn];
int low[maxn];
int dfn[maxn];
int ess[maxm];
int parent[maxn];
int ret;
int n, m;
void dfs(int u)
{
static int essTop = -1;
static int count = 0;
visitV[u] = true;
dfn[u] = low[u] = ++count;
for (int i=0; i<G[u].size(); ++i)
{
int v = G[u][i].b;
int eid = G[u][i].edgeId;
if (visitE[eid]) // 只允許一次入棧
continue;
visitE[eid] = true;
ess[++essTop] = eid;
if (!visitV[v])
{
parent[v] = u;
dfs(v);
low[u] = min(low[u], low[v]);
if (low[v] >= dfn[u])// 找到割點,或者是根節點。彈棧
{
++ret;
int j = essTop;
int minId = eid;
while(ess[j] != eid){
minId = min(minId, ess[j--]);
}
while(j!= essTop)
{
eGroup[ess[essTop--]] = minId;
}
--essTop;
eGroup[eid] = minId;
}
}else if (v != parent[u])
{
low[u] = min(low[u], dfn[v]);
}
}
}
int main()
{
scanf("%d %d", &n, &m);
for (int i=1; i<=m; ++i)
{
int a, b;
scanf("%d %d", &a, &b);
G[a].push_back(E{b, i});
G[b].push_back(E{a, i});
}
memset(visitV, 0, sizeof(visitV));
memset(visitE, 0, sizeof(visitE));
parent[1] = 0;
ret =0;
dfs(1);
printf("%d\n", ret);
for (int i=1; i<= m;++i)
printf("%d ", eGroup[i]);
printf("\n");
return 0;
}