• SCC统计


    Kosoraju

    SCC总数及记录SCC所需要的最少边情况

    #include<cstdio>
    const int N = 300010;
    int scc_cnt = 0;
    int Case, n, m, i, x, y, cnt, use[N], g[2][N], nxt[2][N], v[2][N], ed, q[N], t, vis[N], e[N][2];
    inline void add(int x, int y)
    {
            v[0][++ed] = y;
            nxt[0][ed] = g[0][x];
            g[0][x] = ed;
            v[1][ed] = x;
            nxt[1][ed] = g[1][y];
            g[1][y] = ed;
    }
    void dfs1(int x)
    {
            vis[x] = 1;
            for (int i = g[0][x]; i; i = nxt[0][i])
                    if (!vis[v[0][i]])
                    {
                            use[i] = 1, dfs1(v[0][i]);
                    }
            q[++t] = x;
    }
    void dfs2(int x)
    {
            vis[x] = 0;
            for (int i = g[1][x]; i; i = nxt[1][i])
                    if (vis[v[1][i]])
                    {
                            use[i] = 1, dfs2(v[1][i]);
                    }
    }
    int main()
    {
            scanf("%d", &Case);
            while (Case--)
            {
                    scc_cnt = 0;
                    scanf("%d%d", &n, &m);
                    for (ed = 0, i = 1; i <= n; i++)
                    {
                            vis[i] = g[0][i] = g[1][i] = 0;
                    }
                    for (i = 1; i <= m; i++)
                    {
                            scanf("%d%d", &x, &y);
                            e[i][0] = x, e[i][1] = y;
                            add(x, y);
                            use[i] = 0;
                    }
                    for (t = 0, i = 1; i <= n; i++)
                            if (!vis[i])
                            {
                                    dfs1(i);
                            }
                    for (i = n; i; i--)
                            if (vis[q[i]])
                            {
                                    scc_cnt++;
                                    dfs2(q[i]);
                            }
                    printf("%d
    ",scc_cnt);
                    //                cnt = n * 2;
                    //                for (i = 1; i <= m; i++)if (use[i])
                    //                        {
                    //                                cnt--;
                    //                        }
                    //                for (i = 1; i <= m; i++)if (!use[i] && cnt > 0)
                    //                        {
                    //                                use[i] = 1, cnt--;
                    //                        }
                    //                for (i = 1; i <= m; i++)if (!use[i])
                    //                        {
                    //                                printf("%d %d
    ", e[i][0], e[i][1]);
                    //                        }
            }
    }
    View Code

    Tarjan

    转自:

    https://www.cnblogs.com/stxy-ferryman/p/7779347.html

    https://blog.csdn.net/sentimental_dog/article/details/53790582

    用处:

    1.有向图强连通分量   (一个有向图是强连通的当且仅当G中有一个回路,它至少包含每个节点一次)

    2.无向图双连通分量(割点,桥)

    3.离线LCA

    割顶:若去掉一个点和与这个点相连的边后,图不再连通,则这个点是割顶。
    ​  求法:若节点uu存在一棵子树vv满足vv中所有节点的回边都指向uu及以下的节点(即low[v]pre[u]low[v]≥pre[u]),则uu是割顶;所以一次DFS即可求出所有割顶。但注意一个特殊情况!根节点是割顶当且仅当它的子节点数严格大于1。

    :去掉这条边,图不再连通。
      求法:若节点uu存在一棵子树vv满足vv中所有节点的回边都指向uu以下(不含uu)的节点(即low[v]>pre[u]low[v]>pre[u]),则边(u,v)(u,v)是桥;所以一次DFS即可求出所有桥。

    BCC(点-双连通分量):连通分量中任意两点间都至少有两条“点不重复路径”。
      求法:点双等价于不含割顶的极大子图。但一个问题是割顶本身可能属于多个点双。解决方法是把分开,即保存一个边的栈。

    eBCC(边-双连通分量):连通分量中任意两点间都至少有两条“边不重复路径”。
      求法:边双等价于不含桥的极大子图。一条点不可能分属多个边双(否则可以将这些个边双合并)。所以一次DFS求出所有桥,再来一次DFS把边双分开即可(DFS到桥就不走)。

    前向星版SCC

    void tarjan(int x)
    {
            dfn[x] = ++deep;
            low[x] = deep;
            visit[x] = 1;
            sta[++top] = x;
            for (int i = Head[x]; i; i = nxt[i])
            {
                    int v = to[i];
                    if (!dfn[v])
                    {
                            tarjan(v);
                            low[x] = min(low[x], low[v]);
                    }
                    else
                    {
                            if (visit[v])
                            {
                                    low[x] = min(low[x], low[v]);
                            }
                    }
            }
            if (dfn[x] == low[x])
            {
                    color[x] = ++colorsum;
                    visit[x] = 0;
                    while (sta[top] != x)
                    {
                            color[sta[top]] = colorsum;
                            visit[sta[top--]] = 0;
                    }
                    top--;
            }
    }
    View Code

    [USACO06JAN]The Cow Prom

    算强连通分量中点数不小于1的个数

    /*#include<cstring>#include<algorithm>#include<queue>#include<vector>#include<cstdio>#include<cmath>#include<iostream>*/
    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int maxn = 100005;
    vector<int> gra[maxn];
    int dfn[maxn];//表示这个点在dfs的时候是第几个搜到的;
    int low[maxn];//表示这个点及其子节点连的所有点里dfn的最小值
    int sta[maxn];//存着当前所有可能能构成强连通分量的点
    int visit[maxn];//表示一个点目前是否在sta中
    int cnt[maxn];//各个强连通分量中含点的数目
    int color[maxn];//表示一个点属于哪个强连通分量
    int deep;/*表示从前有多少个点被搜到*/
    int top;/*sta目前的大小*/
    int colorsum = 0;/*目前强连通分量的数目*/
    int ans = 0;
    int n, m;
    void tarjan(int x)
    {
            dfn[x] = ++deep;
            low[x] = deep;
            visit[x] = 1;
            sta[++top] = x;
            int sz = gra[x].size();
            for (int i = 0; i < sz; i++)
            {
                    int to = gra[x][i];
                    if (!dfn[to])
                    {
                            tarjan(to);
                            low[x] = min(low[x], low[to]);
                    }
                    else
                    {
                            if (visit[to])
                            {
                                    low[x] = min(low[x], low[to]);
                            }
                    }
            }
            if (dfn[x] == low[x])
            {
                    color[x] = ++colorsum;
                    visit[x] = 0;
                    while (sta[top] != x)
                    {
                            color[sta[top]] = colorsum;
                            visit[sta[top--]] = 0;
                    }
                    top--;
            }
    }
    int main()
    {
            cin >> n >> m;
            int from, to;
            for (int i = 1; i <= m; i++)
            {
                    cin >>  from >> to;
                    gra[from].push_back(to);
            }
            for (int i = 1; i <= n; i++)
            {
                    if (!dfn[i])
                    {
                            tarjan(i);
                    }
            }
            for (int i = 1; i <= n; i++)
            {
                    cnt[color[i]]++;
            }
            for (int i = 1; i <= colorsum; i++)
            {
                    if (cnt[i] > 1)
                    {
                            ans++;
                    }
            }
            cout << ans << endl;
    }
    View Code

    poj2186 Popular Cows

    缩点后求出度为0的点的个数(如果求得的点为缩点后的超级点需要特判)

    /*#include<cstring>#include<algorithm>#include<queue>#include<vector>#include<cstdio>#include<cmath>#include<iostream>*/
    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int maxn = 100005;
    vector<int> gra[maxn];
    int du[maxn];
    int dfn[maxn];//表示这个点在dfs的时候是第几个搜到的;
    int low[maxn];//表示这个点及其子节点连的所有点里dfn的最小值
    int sta[maxn];//存着当前所有可能能构成强连通分量的点
    int visit[maxn];//表示一个点目前是否在sta中
    int cnt[maxn];//各个强连通分量中含点的数目
    int color[maxn];//表示一个点属于哪个强连通分量
    int deep;/*表示从前有多少个点被搜到*/
    int top;/*sta目前的大小*/
    int colorsum = 0;/*目前强连通分量的数目*/
    int ans = 0;
    int n, m;
    void tarjan(int x)
    {
            dfn[x] = ++deep;
            low[x] = deep;
            visit[x] = 1;
            sta[++top] = x;
            int sz = gra[x].size();
            for (int i = 0; i < sz; i++)
            {
                    int to = gra[x][i];
                    if (!dfn[to])
                    {
                            tarjan(to);
                            low[x] = min(low[x], low[to]);
                    }
                    else
                    {
                            if (visit[to])
                            {
                                    low[x] = min(low[x], low[to]);
                            }
                    }
            }
            if (dfn[x] == low[x])
            {
                    color[x] = ++colorsum;
                    visit[x] = 0;
                    while (sta[top] != x)
                    {
                            color[sta[top]] = colorsum;
                            visit[sta[top--]] = 0;
                    }
                    top--;
            }
    }
    int main()
    {
            int temp = 0;
            cin >> n >> m;
            int from, to;
            for (int i = 1; i <= m; i++)
            {
                    cin >>  from >> to;
                    gra[from].push_back(to);
            }
            for (int i = 1; i <= n; i++)
            {
                    if (!dfn[i])
                    {
                            tarjan(i);
                    }
            }
            for (int i = 1; i <= n; i++)
            {
                    int len = gra[i].size();
                    for (int j = 0; j < len; j++)
                    {
                            int to = gra[i][j];
                            if (color[to] != color[i]) //如果缩点后两个点不在一个强连通分量内
                            {
                                    du[color[i]]++;
                            }
                    }
                    cnt[color[i]]++;
            }
            for (int i = 1; i <= colorsum; i++)
            {
                    if (temp > 1)
                    {
                            cout << 0 << endl;
                            return 0;
                    }
                    if (du[i] == 0)
                    {
                            temp++;
                            ans = cnt[i];
                    }
            }
            cout << ans << endl;
    }
    View Code

    洛谷3388 求割点并输出模板题(附带桥输出)

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int maxn = 100005;
    vector<int> gra[maxn];
    int dfn[maxn];//表示这个点在dfs的时候是第几个搜到的;
    int low[maxn];//表示这个点及其子节点连的所有点里dfn的最小值
    int iscut[maxn];//表示这个点是否是割点
    int deep;/*表示从前有多少个点被搜到*/
    int ans = 0;
    int n, m;
    int tarjan(int x, int pre)
    {
            int child = 0;
            int lowx;
            lowx = dfn[x] = ++deep;
            int len = gra[x].size();
            for (int i = 0; i < len; i++)
            {
                    int to = gra[x][i];
                    if (!dfn[to])
                    {
                            child++;
                            int lowto = tarjan(to, x);
                            lowx = min(lowx, lowto);
                            if (lowto >= dfn[x])
                            {
                                    iscut[x] = 1;
                            }
                            //                        if (lowto > dfn[x])
                            //                        {
                            //                                cout << "bridge " << x << " " << to << endl;
                            //                        }
                    }
                    else
                    {
                            if (to != pre && dfn[to] < dfn[x])
                            {
                                    lowx = min(lowx, dfn[to]);
                            }
                    }
            }
            if (pre < 0 && child == 1)
            {
                    iscut[x] = 0;
            }
            low[x] = lowx;
            return lowx;
    }
    int main()
    {
            int temp = 0;
            cin >> n >> m;
            int from, to;
            for (int i = 1; i <= m; i++)
            {
                    cin >>  from >> to;
                    gra[from].push_back(to);
                    gra[to].push_back(from);
            }
            for (int i = 1; i <= n; i++)
            {
                    if (!dfn[i])
                    {
                            tarjan(i, -1);
                    }
            }
            for (int i = 1; i <= n; i++)
            {
                    if (iscut[i])
                    {
                            ans++;
                    }
            }
            cout << ans << endl;
            for (int i = 1; i <= n; i++)
            {
                    if (iscut[i])
                    {
                            cout << i << " ";
                    }
            }
            cout << endl;
    }
    View Code

    Codeforces 962 F

    求点双连通分量中的边

    /*Huyyt*/
    #include<bits/stdc++.h>
    #define mem(a,b) memset(a,b,sizeof(a))
    #define pb push_back
    using namespace std;
    typedef long long ll;
    typedef unsigned long long ull;
    const int dir[8][2] = {{0, 1}, {1, 0}, {0, -1}, { -1, 0}, {1, 1}, {1, -1}, { -1, -1}, { -1, 1}};
    const int mod = 1e9 + 7;
    const int gakki = 5 + 2 + 1 + 19880611 + 1e9;
    const int MAXN = 2e5 + 5;
    const int MAXM = 2e5 + 5;
    int to[MAXM << 1], nxt[MAXM << 1], Head[MAXN], tot = 1;
    inline void addedge(int u, int v)
    {
            to[++tot] = v;
            nxt[tot] = Head[u];
            Head[u] = tot;
    }
    int n, m;
    int dfn[MAXN], low[MAXN], dfs_clock = 0;
    int BCCcnt = 0, blong[MAXN], inque[MAXM << 1];
    int st[MAXN], l = 0, ans[MAXN], ansnum = 0;
    bool vis[MAXM << 1];
    void tarjanBCC(int x, int fa)
    {
            dfn[x] = low[x] = ++dfs_clock;
            for (int i = Head[x]; i; i = nxt[i])
            {
                    int v = to[i];
                    if (v == fa || vis[i])
                    {
                            continue;
                    }
                    vis[i] = vis[i ^ 1] = 1;
                    st[l++] = i;
                    if (!dfn[v])
                    {
                            tarjanBCC(v, x);
                            low[x] = min(low[v], low[x]);
                            if (dfn[x] <= low[v])
                            {
                                    int now, vnumber = 0, enumber = 0;
                                    BCCcnt++;
                                    while(1)
                                    {
                                            now = st[--l];
                                            if (blong[to[now]] != BCCcnt)
                                            {
                                                    blong[to[now]] = BCCcnt, ++vnumber;
                                            }
                                            if (blong[to[now ^ 1]] != BCCcnt)
                                            {
                                                    blong[to[now ^ 1]] = BCCcnt, ++vnumber;
                                            }
                                            inque[++enumber] = now;
                                            if(now==i)
                                            break;
                                    }
                                    if (vnumber == enumber)
                                    {
                                            for (int i = 1; i <= enumber; i++)
                                            {
                                                    ans[++ansnum] = inque[i] / 2;
                                            }
                                    }
                            }
                    }
                    else
                    {
                            low[x] = min(low[x], dfn[v]);
                    }
            }
    }
    int main()
    {
            ios_base::sync_with_stdio(0);
            cin.tie(0);
            cin >> n >> m;
            int u, v;
            for (int i = 1; i <= m; i++)
            {
                    cin >> u >> v;
                    addedge(u, v), addedge(v, u);
            }
            for (int i = 1; i <= n; i++)
            {
                    if (!dfn[i])
                    {
                            tarjanBCC(i, -1);
                    }
            }
            sort(ans + 1, ans + 1 + ansnum);
            cout << ansnum << endl;
            for (int i = 1; i <= ansnum; i++)
            {
                    cout << ans[i] << " ";
            }
            return 0;
    }
    View Code

    附:求边双连通分量中的边

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    const int N=1e5+88;
    int dfn[N],low[N],H[N],nxt[N<<1],to[N<<1];
    int n,m,x,y,cnt,tot=1;
    bool ib[N],is[N];
    void add(int x,int y){
        to[++tot]=y;nxt[tot]=H[x];H[x]=tot;
    }
    void dfs(int u,int fa){
        low[u]=dfn[u]=++cnt;
        int chi=0;
        for(int i=H[u];i;i=nxt[i]){
            int v=to[i];
            if(v==fa) continue;
            if(!dfn[v]) {
                ++chi;
                dfs(v,u);
                low[u]=min(low[u],low[v]);
                if(low[v]>dfn[u]) ib[i]=ib[i^1]=1;
                if(low[v]>=dfn[u]) is[u]=1;
            } 
            else low[u]=min(low[u],dfn[v]);
        }
        if(chi==1&&fa==-1) is[u]=0;
    }
    int num,bnum,a[N],A[N],ans;
    void dfs2(int u,int fa){
        ++num;for(int i=H[u];i;i=nxt[i]) {
           int v=to[i];
           if(ib[i]||ib[i^1]||v==fa) continue;
           ib[i]=ib[i^1]=1;
           a[++bnum]=i>>1;
           if(!dfn[v]) dfn[v]=1,dfs2(v,u);
        }
    }
    int main(){
        scanf("%d%d",&n,&m);
        for(int i=1;i<=m;++i) {
            scanf("%d%d",&x,&y);
            add(x,y);
            add(y,x);
        }
        for(int i=1;i<=n;++i) if(!dfn[i]) dfs(i,-1);
        for(int i=0;i<=n;++i) dfn[i]=0;
        for(int i=1;i<=n;++i) if(!dfn[i]) {
            num=bnum=0;
            dfn[i]=1;
            dfs2(i,-1);
            if(num==bnum) for(int j=1;j<=num;++j) A[++ans]=a[j];
        }
        printf("%d
    ",ans);
        sort(A+1,A+ans+1);
        for(int i=1;i<=ans;++i) printf("%d ",A[i]);
    }
    View Code 
  • 相关阅读:
    装饰器
    FLASK
    Flask第一个实例
    各种各样的PyQt测试和例子
    项目实战:天气信息查询
    窗口设置、QSS
    槽和信号
    布局
    打印机
    菜单栏、工具栏、状态栏
  • 原文地址:https://www.cnblogs.com/Aragaki/p/8933724.html
Copyright © 2020-2023  润新知