题目
题意
给一个无向无自环无重边不连通的图,求所有简单环上的所有边。
思路
考虑点双。
点双跑出来只要判断每个块是否点数==边数。
栈里记录边起点和终点和编号,并且将反边也加入到栈里,就可以得到每个分量的边和点。
/* Author : Rshs
* Data : 2019-10-02-14.51
*/
#include<bits/stdc++.h>
using namespace std;
#define FI first
#define SE second
#define LL long long
#define MP make_pair
#define PII pair<int,int>
#define SZ(a) (int)a.size()
const double pai = acos(-1);
const double eps = 1e-10;
const LL mod = 1e9+7;
const int MXN = 1e6+5;
vector<PII>g[MXN];
vector<int>nnn[MXN];
vector<int>bcc[MXN];
int ec[MXN];
/* 点双分量 */
int dfn[MXN],iscut[MXN],dfs_clock,bcc_cnt,bccno[MXN];
/*先序序号 割点否 时间戳 点双数量*/
struct no{
int x,y,p;
};
stack<no>sk;
/* 暂存儿子*/
int tarjan(int u,int fa){
int lowu=dfn[u]=++dfs_clock;
int child=0;//记录u的儿子数
for(auto ii:g[u]){
int v=ii.FI;
no E=no{u,ii.FI,ii.SE};
if(!dfn[v]){
sk.push(E);
child++;
int lowv=tarjan(v,u);
lowu=min(lowu,lowv);
if(lowv>=dfn[u]){ //u是割点,u及v为根的子树是点双
bcc_cnt++;
iscut[u]=1;
while(1){
no now=sk.top();sk.pop();
ec[bcc_cnt]++;nnn[bcc_cnt].push_back(now.p);
if(bccno[now.x]!=bcc_cnt){bcc[bcc_cnt].push_back(now.x);bccno[now.x]=bcc_cnt;}
if(bccno[now.y]!=bcc_cnt){bcc[bcc_cnt].push_back(now.y);bccno[now.y]=bcc_cnt;}
if(now.x==u&&now.y==v) break;
}
}
}
else if(v!=fa&&dfn[v]<dfn[u]){
lowu=min(lowu,dfn[v]); //连到了u的祖先(反向边
sk.push(E);
}
}
if(fa<0&&child==1) iscut[u]=0; //处理根结点
return lowu;
}
void bccGet(int n){
for(int i=1;i<=n;i++)bcc[i].clear(),dfn[i]=iscut[i]=0;
while(!sk.empty())sk.pop();
for(int i=1;i<=n;i++) if(!dfn[i])tarjan(i,-1);
}
int main(){
int n,m;cin>>n>>m;
for(int i=1;i<=m;i++){
int sa,sb;scanf("%d %d",&sa,&sb);
g[sa].push_back(MP(sb,i));g[sb].push_back(MP(sa,i));
}
bccGet(n);
vector<int>ans;
for(int i=1;i<=bcc_cnt;i++){
if(SZ(bcc[i])!=ec[i]||ec[i]<=2)continue;
for(auto j:nnn[i]) ans.push_back(j);
}
sort(ans.begin(),ans.end());
cout<<ans.size()<<endl;
for(auto i:ans)cout<<i<<' ';
return 0;
}