poj3694 network(边双联通分量+lca+并查集)

题    目    传    送    们    在    这


 题目大意

有一个由n个点和m条边组成的无向联通图。

现在有Q个操作,每次操作可以在点x,y之间连一条边。

问你每次操作后有多少个多少个桥(即删掉后就会使图不联通的边)。

解题思路

根据边双联通的定义,我们知道将边双联通分量缩点后的图,其中的边即为桥。

我们将这个图缩点,就变成了一棵树。

而每次在两个不同的边双联通分量x,y之间加边后,就出现了一个包含x,y的环,其中原先这颗树上x,y的树上最短路径就不在是边。

所以对于每个x,y,我们用倍增求出它的最近公共祖先z,其中包含的桥的数量为x的深度+y的深度-2*z的深度。

但是需要注意的是有些边会被算过多次,我们可以将这些边标记一下,然后每次直接dfs走一遍x和y到z的路径进行统计。

而我这里用了并查集进行优化,分别跑一遍x到z和y到z的路径,将每个点都指向父节点,然后进行路径合并,下次再次统计的时候会直接跳过。

代码如下:

 

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <vector>
#include <cmath>
#define rep(x, l, r) for(int x = l; x <= r; x++)
#define repd(x, r, l) for(int x = r; x >= l; x--)
#define clr(x, y) memset(x, y, sizeof(x))
#define all(x) x.begin(), x.end()
#define pb push_back
#define mp make_pair
#define MAXN 300005
#define MAXM 1200005
#define fi first
#define se second
#define SZ(x) ((int)x.size())
using namespace std;
typedef long long LL;
typedef vector<int> vi;
typedef pair<int, int> pii;
const int INF = 0x3f3f3f3f;
const int p = 10000007;
int lowbit(int x){ return x & -x; }
int fast_power(int a, int b){ int x; for(x = 1; b; b >>= 1){ if(b & 1) x = 1ll * x * a % p; a = 1ll * a * a % p; } return x; }

int n, t, cnt, cnt2, num, dcc;
int head[MAXN], nxt[MAXM], to[MAXM];
int head2[MAXN], nxt2[MAXM], to2[MAXM];
int dfn[MAXN], low[MAXN], c[MAXN];
bool bge[MAXN];
int deep[MAXN], fa[MAXN][25], fa2[MAXN];

void init(){
    cnt = 0;
    clr(head, -1);
    cnt2 = 0;
    clr(head2, -1);
    clr(dfn, 0);
    clr(low, 0);
    clr(bge, 0);
    clr(c, 0);
    clr(fa, 0);
    clr(deep, 0);
    dcc = 0;
    num = 0;
}

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

void tarjan(int u, int in_edge){
    dfn[u] = low[u] = ++num;
    for(int e = head[u]; e != -1; e = nxt[e]){
        int v = to[e];
        if(!dfn[v]){
            tarjan(v, e);
            low[u] = min(low[u], low[v]);
            if(dfn[u] < low[v]) bge[e] = bge[e ^ 1] = 1;
        }
        else if(e != (in_edge ^ 1))
            low[u] = min(low[u], dfn[v]);
    }
}

void dfs(int u){
    c[u] = dcc;
    for(int e = head[u]; e != -1; e = nxt[e]){
        int v = to[e];
        if(c[v] || bge[e]) continue;
        dfs(v);
    }
}

void addedge2(int u, int v){
    nxt2[cnt2] = head2[u];
    head2[u] = cnt2;
    to2[cnt2] = v;
    cnt2++;
}

void dfs2(int u){
    for(int e = head2[u]; e != -1; e = nxt2[e]){
        int v = to2[e];
        if(!deep[v]){
            fa[v][0] = u;
            deep[v] = deep[u] + 1;
            dfs2(v);
        }
    }
}

void init2(){
    rep(j, 1, t)
        rep(i, 1, n) fa[i][j] = fa[fa[i][j - 1]][j - 1];
}

int lca(int x, int y){
    if(deep[x] < deep[y]) swap(x, y);
    repd(i, t, 0)
        if(deep[fa[x][i]] >= deep[y]) x = fa[x][i];
    if(x == y) return x;
    repd(i, t, 0)
        if(fa[x][i] != fa[y][i]){
            x = fa[x][i];
            y = fa[y][i];
        }
    return fa[x][0];
}

int find(int x){
	if (x == fa2[x]) return x;
	return fa2[x] = find(fa2[x]);
}

int main(){
    int m;
    int times = 0;
    while(~scanf("%d%d", &n, &m) && n && m){
        printf("Case %d:\n", ++times);
        init();
        t = log2(n) + 1;
        rep(i, 1, m){
            int u, v;
            scanf("%d%d", &u, &v);
            addedge(u, v);
            addedge(v, u);
        }
        rep(i, 1, n)
            if(!dfn[i]) tarjan(1, -1);
        rep(i, 1, n){
            if(!c[i]){
                ++dcc;
                dfs(i);
            }
        }
        rep(i, 0, cnt - 1){
            int u = to[i ^ 1], v = to[i];
            if(c[u] == c[v]) continue;
            addedge2(c[u], c[v]);
        }
        deep[1] = 1;
        dfs2(1);
        init2(); 
        int k;
        scanf("%d", &k);
        int ans = dcc - 1;
        rep(i, 1, dcc) fa2[i] = i;
        rep(i, 1, k){
            int u, v;
            scanf("%d%d", &u, &v);
            int x = c[u], y = c[v], father = lca(x, y);
            x = find(x);
            while(deep[x] > deep[father]){
                fa2[x] = fa[x][0];
                ans--;
                x = find(x);
            }
            y = find(y);
            while(deep[y] > deep[father]){
                fa2[y] = fa[y][0];
                ans--;
                y = find(y);
            }       
            printf("%d\n", ans);
        }
        puts("");
    }
    return 0;
}

 

转载于:https://www.cnblogs.com/SHANAchan/p/10351889.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值