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;
}
posted @ 2018-04-19 18:19  v9fly  阅读(364)  评论(0编辑  收藏  举报