• 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
    ", ans.size());
        for(int v : ans) printf("%d ", v);
    
        return 0;
    }
    
  • 相关阅读:
    Java Sping 第一章——初识 Spring
    C++设计模式——状态模式 State
    线性代数思维导图(3)——向量组
    基于Servlet实现简单系统登录
    优秀博客汇总
    整理一些开源项目
    Android UI性能优化详解
    (原创)如何在spannableString中使用自定义字体
    (原创)用讯飞语音实现人机交互的功能
    (原创)speex与wav格式音频文件的互相转换(二)
  • 原文地址:https://www.cnblogs.com/sbfhy/p/8884856.html
Copyright © 2020-2023  润新知