codeforces 962F 点双连通分量
F. Simple Cycles Edges
题意:
n 个点 m 条边的无向图,问哪些边是恰好包含在一个简单环里的。简单环定义:环走一遍,每个点都只出现一次。
tags:
考虑 tarjan 缩点,要包含在简单环里,就是点双连通。
最后在每个点双连通分量里,如边数和点数相同,那就符合。
// 962F
#include<bits/stdc++.h>
using namespace std;
#pragma comment(linker, "/STACK:102400000,102400000")
#define rep(i,a,b) for (int i=a; i<=b; ++i)
#define per(i,b,a) for (int i=b; i>=a; --i)
#define mes(a,b) memset(a,b,sizeof(a))
#define INF 0x3f3f3f3f
#define MP make_pair
#define PB push_back
#define fi first
#define se second
typedef long long ll;
const int N = 100005, M = N*2;
struct Edge {
int to,next;
Edge() {}
Edge(int to,int next) { this->to=to; this->next=next; }
}ed[M];
int head[N], lnum, esum, index, top, bccnum;
int dfn[N], low[N], bj[N], Belong[M], num[N];
bool mark[M], isCut[M];
stack<int > Stack;
pair<int, int> E[M];
vector< int > e2[M];
void Addedge(int u,int v) { ed[lnum]=Edge(v, head[u]); head[u]=lnum++; }
void Init_BCC() {
memset(head, -1, sizeof(head));
memset(dfn, 0, sizeof(dfn));
memset(low, 0, sizeof(low));
memset(mark, false, sizeof(mark));
memset(Belong, 0, sizeof(Belong));
memset(isCut, false, sizeof(isCut));
while(!Stack.empty()) Stack.pop();
lnum=0;
index=0;
bccnum=0;
esum=0;
}
void Tarjan(int u,int fa)
{ //有自环时不加自环的边
//点双连通缩点方法:清空路径,枚举E[]数组中存储的路径,建立双向边。
dfn[u] = low[u] = ++index;
int child = 0;
for(int i=head[u]; ~i; i=ed[i].next)
{
if(mark[i]) continue;
int v=ed[i].to;
mark[i] = mark[i^1] = true;
Stack.push(i); //边入栈,需注意此语句要放在判continue之后
if(!dfn[v])
{
++child;
Tarjan(v, u);
low[u]=min(low[u],low[v]);
if(dfn[u]<=low[v])
{
isCut[u] = true;
bccnum++; //注意这里是N++,建数组时要注意开至少两倍大
while(true)
{
int j=Stack.top(); Stack.pop();
//bj[]数组用来标记节点所属的bcc,割点会改变,无意义
//E[]存新图的边,esum是其数量,tarjan结束后建双向边
if(bj[ed[j].to]!=bccnum){
bj[ed[j].to] = bccnum;
num[bccnum]++;
E[++esum] = make_pair(ed[j].to, bccnum); // 新图中 ed[j].to -> bccnum+n
}
if(bj[ed[j^1].to]!=bccnum){
bj[ed[j^1].to] = bccnum;
num[bccnum]++;
E[++esum] = make_pair(ed[j^1].to, bccnum);
}
Belong[(j>>1)+1]=bccnum; //标记边所属的bcc
e2[bccnum].PB((j>>1)+1);
if(i==j) break;
}
}
}
else low[u]=min(low[u],dfn[v]); //与有向图区分,此处else不需要判别v节点是否在栈内
}
if(fa<0 && child<2) isCut[u]=false; //如果初始节点没有2个以上儿子,标记清0
}
vector< int > ans;
int n, m;
int main()
{
Init_BCC();
scanf("%d%d", &n, &m);
int u, v;
rep(i,1,m)
{
scanf("%d%d", &u, &v);
Addedge(u, v); Addedge(v, u);
}
rep(i,1,n) if(!dfn[i]) Tarjan(i, -1);
rep(i,1,bccnum)
if(num[i]==e2[i].size()) {
for(int v : e2[i]) ans.PB(v);
}
sort(ans.begin(), ans.end());
printf("%d\n", ans.size());
for(int v : ans) printf("%d ", v);
return 0;
}