[Aizu-1388] Counting Cycles

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u013578420/article/details/82146761

题目链接: https://vjudge.net/problem/Aizu-1388

题目大意:
给你一个n个点m条边的无向图, 求简单环(度数为2的连通子图)个数。 ( n 10 5 , n 1 m n + 15 , )

题目思路:
由于m的数据范围特性, 可以考虑先搞一个生成树, 用2^16去枚举所有非树边集合, 判断是否构成一个简单环。
先将非树边上的点设为关键点, 对原图求一个虚树, 这样就大大降低了判环的复杂度。 对于选取的非树边的端点, 可以看会将到根路径上的边取一遍异或, 最后仍为1的边将是树中将这些非树边连接起来的边, 对这些边先是判断每个点度数是否都为2, 在判断连通行即可。

Code:

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <queue>

#define pi pair<ll, int>
#define fi first
#define se second
#define mp make_pair

using namespace std;

#define ll long long

const int N = (int)2e5 + 10;

int gi(){
    char c = getchar(); int ret = 0;
    while (!isdigit(c)) c = getchar();
    while (isdigit(c)){
        ret = ret * 10 + c - '0';
        c = getchar();
    }
    return ret;
}

int n, m;
int edge[N][2]; bool tree[N];
int cnt = 1, lst[N], nxt[N * 2], to[N * 2];

void add(int u, int v){
    nxt[++ cnt] = lst[u]; lst[u] = cnt; to[cnt] = v;
    nxt[++ cnt] = lst[v]; lst[v] = cnt; to[cnt] = u;
}

bool vis[N]; int indx, id[N], fa[N][20], dep[N];
void dfs(int u){
    vis[u] = 1; id[u] = ++ indx;
    for (int j = lst[u]; j; j = nxt[j]){
        int v = to[j];
        if (vis[v]) continue;
        fa[v][0] = u; dep[v] = dep[u] + 1;
        dfs(v); tree[j >> 1] = 1;
    }
}
void PreLca(){
    for (int j = 1; j < 20; j ++)
        for (int i = 1; i <= n; i ++)
            fa[i][j] = fa[fa[i][j - 1]][j - 1];
}
int lca(int u, int v){
    if (dep[u] < dep[v]) swap(u, v);
    for (int k = dep[u] - dep[v], j = 0; k; j ++, k >>= 1)
        if (k & 1) u = fa[u][j];
    if (u == v) return u;
    for (int j = 19; j >= 0; j --)
        if (fa[u][j] != fa[v][j]) u = fa[u][j], v = fa[v][j];
    return fa[u][0];
}

int mt, nt, E[N][2], nd[N], vfa[N]; bool key[N];
bool cmp(int i, int j){return id[i] < id[j];}

int top, stk[N];
void vadd(int u, int v){vfa[v] = u;}
void BuildVT(){
    sort(nd + 1, nd + nt + 1, cmp);

    int _nt = nt;
    for (int i = 1; i <= nt; i ++){
        int x = nd[i];
        if (!top){stk[++ top] = x; continue;}

        int plca = lca(stk[top], x);
        while (dep[stk[top - 1]] > dep[plca] && top > 1){
            vadd(stk[top - 1], stk[top]);
            top --;
        }
        if (dep[plca] < dep[stk[top]]){
            vadd(plca, stk[top]);
            top --;
        }
        if (!top || dep[stk[top]] < dep[plca]) stk[++ top] = plca;
        stk[++ top] = x;
        if (!key[plca]) {key[plca] = 1; nd[++ _nt] = plca;}
    }
    while (top > 1){
        vadd(stk[top - 1], stk[top]);
        top --;
    }

    nt = _nt;
}

bool mark[N]; int deg[N];
int _cnt, _tim, _lst[N], _nxt[N], _to[N], _flag[N], _vis[N];
void _add(int u, int v){
    deg[u] ++; deg[v] ++;
    if (_flag[u] != _tim) _lst[u] = 0, _flag[u] = _tim;
    if (_flag[v] != _tim) _lst[v] = 0, _flag[v] = _tim;
    _nxt[++ _cnt] = _lst[u]; _lst[u] = _cnt; _to[_cnt] = v;
    _nxt[++ _cnt] = _lst[v]; _lst[v] = _cnt; _to[_cnt] = u;
}

void _dfs(int u){
    if (_vis[u] == _tim) return;
    _vis[u] = _tim;

    for (int j = _lst[u]; j; j = _nxt[j]){
        int v = _to[j];

        _dfs(v);
    }
}

void work(){
    int ans = 0;

    for (int s = 1; s < (1 << mt); s ++){
        for (int i = 1; i <= nt; i ++) mark[nd[i]] = 0, deg[nd[i]] = 0;
        _cnt = 0;
        _tim ++;

        for (int i = 1; i <= mt; i ++)
            if ((s >> (i - 1)) & 1){
                for (int p = E[i][0]; p; p = vfa[p]) mark[p] ^= 1;
                for (int p = E[i][1]; p; p = vfa[p]) mark[p] ^= 1;

            }


        for (int i = 1; i <= mt; i ++)
            if ((s >> (i - 1)) & 1){
                _add(E[i][0], E[i][1]);
            }

        for (int i = 1; i <= nt; i ++)
            if (mark[nd[i]] && vfa[nd[i]]) {
                _add(nd[i], vfa[nd[i]]);
            }

        bool ok = 1;
        for (int i = 1; i <= nt && ok; i ++)
            if (deg[nd[i]] && deg[nd[i]] != 2) ok = 0;

        for (int i = 1; i <= nt && ok; i ++)
            if (deg[nd[i]]) {_dfs(nd[i]); break;}

        for (int i = 1; i <= nt && ok; i ++)
            if (deg[nd[i]] && _vis[nd[i]] != _tim) ok = 0;

        if (ok) ans ++;  
    }
    printf("%d\n", ans);
}

int main()
{

    n = gi(); m = gi();
    for (int i = 1; i <= m; i ++){
        edge[i][0] = gi(); edge[i][1] = gi();
        add(edge[i][0], edge[i][1]);
    }

    dfs(1);
    PreLca();

    for (int i = 1; i <= m; i ++)
        if (!tree[i]) {
            ++ mt;
            E[mt][0] = edge[i][0], E[mt][1] = edge[i][1];
            if (!key[edge[i][0]]) key[edge[i][0]] = 1, nd[++ nt] = edge[i][0];
            if (!key[edge[i][1]]) key[edge[i][1]] = 1, nd[++ nt] = edge[i][1];
            //printf("%d %d\n", E[mt][0], E[mt][1]);
        }

    BuildVT();
    work();

    return 0;
}

猜你喜欢

转载自blog.csdn.net/u013578420/article/details/82146761