POJ 3694 Tarjan e-DCC + LCA + 并查集

题意

传送门 POJ 3694 Network

题解

朴素做法是每次添加边使用 T a r j a n Tarjan Tarjan 算法求一次割边,总时间复杂度 O ( Q ( N + M ) ) O(Q(N+M)) O(Q(N+M))

实际上被减少的割边不会再次计入。 T a r j a n Tarjan Tarjan 求解割边, e − D C C e-DCC eDCC 缩点,构成一棵树。每次查询,将 ( x , l c a ( x , y ) ) , ( l c a ( x , y ) , y ) (x,lca(x,y)),(lca(x,y),y) (x,lca(x,y)),(lca(x,y),y) 路径上的边减去,同时使用并查集维护当前位置所指向的第一条未减去的割边,此时保证每条边只被减去一次。总时间复杂度 O ( M + N log ⁡ N ) O(M+N\log N) O(M+NlogN)

#include <algorithm>
#include <cstdio>
#include <cstring>
using namespace std;
const int maxn = 100005, maxm = 2 * 200005;
int T, N, M, Q, num, dcc, res, dfn[maxn], low[maxn], dc[maxn];
int tot, head[maxn], to[maxm], nxt[maxm];
bool bg[maxm];
int tot_c, hc[maxn], tc[maxm], nc[maxm];
int par[maxn], fa[maxn], dep[maxn];

inline void add(int x, int y) {
    
     to[++tot] = y, nxt[tot] = head[x], head[x] = tot; }

inline void add_c(int x, int y) {
    
     tc[++tot_c] = y, nc[tot_c] = hc[x], hc[x] = tot_c; }

void init()
{
    
    
    num = dcc = 0, tot = tot_c = 1;
    memset(head, 0, sizeof(head)), memset(hc, 0, sizeof(hc));
    memset(dfn, 0, sizeof(dfn)), memset(dc, 0, sizeof(dc));
    memset(bg, 0, sizeof(bg));
}

void tarjan(int x, int in)
{
    
    
    dfn[x] = low[x] = ++num;
    for (int i = head[x]; i; i = nxt[i])
    {
    
    
        int y = to[i];
        if (!dfn[y])
        {
    
    
            tarjan(y, i);
            low[x] = min(low[x], low[y]);
            if (low[y] > dfn[x])
                bg[i] = bg[i ^ 1] = 1;
        }
        else if (i != (in ^ 1))
            low[x] = min(low[x], dfn[y]);
    }
}

void dfs(int x)
{
    
    
    dc[x] = dcc;
    for (int i = head[x]; i; i = nxt[i])
    {
    
    
        int y = to[i];
        if (!dc[y] && !bg[i])
            dfs(y);
    }
}

int find(int x) {
    
     return par[x] == x ? x : (par[x] = find(par[x])); }

void dfs(int x, int f, int d)
{
    
    
    fa[x] = f, dep[x] = d;
    for (int i = hc[x]; i; i = nc[i])
    {
    
    
        int y = tc[i];
        if (y != f)
            dfs(y, x, d + 1);
    }
}

void lca(int x, int y)
{
    
    
    for (;;)
    {
    
    
        x = find(x), y = find(y);
        if (x == y)
            break;
        dep[x] > dep[y] ? par[x] = fa[x] : par[y] = fa[y];
        --res;
    }
}

int main()
{
    
    
    while (~scanf("%d%d", &N, &M) && (N | M))
    {
    
    
        printf("Case %d:\n", ++T);
        init();
        for (int i = 1, x, y; i <= M; ++i)
            scanf("%d%d", &x, &y), add(x, y), add(y, x);
        tarjan(1, 0);
        for (int i = 1; i <= N; ++i)
            if (!dc[i])
                ++dcc, dfs(i);
        for (int i = 2; i <= tot; ++i)
        {
    
    
            int x = dc[to[i ^ 1]], y = dc[to[i]];
            if (x != y)
                add_c(x, y);
        }
        dfs(1, 0, 0), res = dcc - 1;
        for (int i = 1; i <= dcc; ++i)
            par[i] = i;
        scanf("%d", &Q);
        for (int i = 1, x, y; i <= Q; ++i)
        {
    
    
            scanf("%d%d", &x, &y);
            lca(dc[x], dc[y]);
            printf("%d\n", res);
        }
        putchar('\n');
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/neweryyy/article/details/114905063
今日推荐