卧薪尝胆,厚积薄发。
Simple Cycles Edges
Date: Sun Jun 24 07:42:27 CST 2018 In Category: NoCategory

Description:

无向图无重边自环,问有哪些边恰好在一个简单环内。
$1\le n\le 100000$ $1\le m\le min(n\times(n-1)/2,100000)$

Solution:

对点双连通分量缩点,统计每个点双连通分量中边的个数,如果点数 $=$ 边数,则一定在简单环中,点数方便统计,只要在求点双弹栈时 $++siz$ 即可,注意每一次 $tarjan$ 完后要再手动 $pop$ 一次,再维护一个边的栈,每次经过一条边时 $push$ ,注意无向边只走一个方向,找到一个点双时 $pop$ 。

Code:


#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<vector>
#include<stack>
#include<cstring>
using namespace std;
int n,m;
#define MAX 100010
int ans = 0;
struct edge
{
int to,nxt,id;
}e[MAX << 1];
int edgenum = 0;
int lin[MAX] = {0};
void add(int a,int b,int c)
{
++edgenum;e[edgenum].to = b;e[edgenum].id = c;e[edgenum].nxt = lin[a];lin[a] = edgenum;
++edgenum;e[edgenum].to = a;e[edgenum].id = c;e[edgenum].nxt = lin[b];lin[b] = edgenum;
return;
}
int dcc = 0;
vector<int> dcce[MAX];
int siz[MAX];
int dfn[MAX],low[MAX],tot = 0;
bool tag[MAX],used[MAX];
stack<int> ps;
stack<int> es;
void tarjan(int k)
{
dfn[k] = low[k] = ++tot;
ps.push(k);
for(int i = lin[k];i != 0;i = e[i].nxt)
{
if(used[e[i].id])continue;
used[e[i].id] = true;es.push(e[i].id);
if(dfn[e[i].to] == 0)
{
tarjan(e[i].to);
low[k] = min(low[k],low[e[i].to]);
if(low[e[i].to] >= dfn[k])
{
++dcc;
int t;
do
{
t = ps.top();ps.pop();
++siz[dcc];
}while(t != e[i].to);
++siz[dcc];
do
{
t = es.top();es.pop();
dcce[dcc].push_back(t);
}while(e[i].id != t);
}
}
else low[k] = min(low[k],dfn[e[i].to]);
}
return;
}
int main()
{
scanf("%d%d",&n,&m);
int a,b;
for(int i = 1;i <= m;++i)
{
scanf("%d%d",&a,&b);
add(a,b,i);
}
for(int i = 1;i <= n;++i)
{
if(dfn[i] == 0)
{
tarjan(i);
ps.pop();
}
}
for(int i = 1;i <= dcc;++i)
{
if(siz[i] == dcce[i].size())
{
for(int j = 0;j < dcce[i].size();++j)
{
tag[dcce[i][j]] = true;
++ans;
}
}
}
cout << ans << endl;
for(int i = 1;i <= m;++i)
{
if(tag[i])
{
printf("%d ",i);
}
}
puts("");
return 0;
}
Copyright © 2020 wjh15101051
ღゝ◡╹)ノ♡