POJ - 3694 Network(割边+LCA)

Network
Time Limit: 5000MS Memory Limit: 65536K
Total Submissions: 11971 Accepted: 4406

Description

A network administrator manages a large network. The network consists of N computers and M links between pairs of computers. Any pair of computers are connected directly or indirectly by successive links, so data can be transformed between any two computers. The administrator finds that some links are vital to the network, because failure of any one of them can cause that data can't be transformed between some computers. He call such a link a bridge. He is planning to add some new links one by one to eliminate all bridges.

You are to help the administrator by reporting the number of bridges in the network after each new link is added.

Input

The input consists of multiple test cases. Each test case starts with a line containing two integers N(1 ≤ N ≤ 100,000) and M(N - 1 ≤ M ≤ 200,000).
Each of the following M lines contains two integers A and B ( 1≤ A ≠ B ≤ N), which indicates a link between computer A and B. Computers are numbered from 1 to N. It is guaranteed that any two computers are connected in the initial network.
The next line contains a single integer Q ( 1 ≤ Q ≤ 1,000), which is the number of new links the administrator plans to add to the network one by one.
The i-th line of the following Q lines contains two integer A and B (1 ≤ A ≠ B ≤ N), which is the i-th added new link connecting computer A and B.

The last test case is followed by a line containing two zeros.

Output

For each test case, print a line containing the test case number( beginning with 1) and Q lines, the i-th of which contains a integer indicating the number of bridges in the network after the first i new links are added. Print a blank line after the output for each test case.

Sample Input

3 2
1 2
2 3
2
1 2
1 3
4 4
1 2
2 1
2 3
1 4
2
1 2
3 4
0 0

Sample Output

Case 1:
1
0

Case 2:
2
0

Source

2008 Asia Hefei Regional Contest Online by USTC

题意:给出一个无向图,有重复的边,然后Q次添加新的边,每添加一次边,输出无向图桥的个数。

思路:每添加一次边用一次求割边算法求割边肯定会T,首先标记出来所有的桥,我们可以把每一个边连通分量看成一个整体块,块与块之间相连的就是桥,所以形成了一颗树,如果我们添加的新的边在同一个块里,肯定不会影响桥的数量,添加的新边在不同的块里,那么添加的边的两点到这两点的最近公共祖先的所以桥都变成了非桥。思路很好理解,但是用代码写出来却不容易,把边连通块缩成一个点可以用并查集,其实不必显式的缩点,因为在求割边深搜时可以形成一颗深度搜索树,另外就是求LCA,朴素的LCA算法。

#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <vector>

using namespace std;

const int MAXN = 100010;
int bridge[MAXN];//bridge[i]=1表示i与father[i]是割边
int pre[MAXN];//dfs序
int father[MAXN];//深搜时形成的深搜树
int dfs_clock,cnt_bri;
vector<int> G[MAXN];

int dfs(int u,int fa)
{
    int lowu;
    int flag = 0;
    lowu = pre[u] = ++dfs_clock;
    for(int i = 0; i < G[u].size(); i++) {
        int v = G[u][i];
        //处理重复的边
        if(v == fa && !flag) {
            flag++;
            continue;
        }
        if(!pre[v]) {
            father[v] = u;
            int lowv = dfs(v,u);

            lowu = min(lowu,lowv);
            if(lowv > pre[u]) {
                cnt_bri++;
                bridge[v] = 1;
            }
        }
        else if(pre[v] < pre[u] && v != fa) {
            lowu = min(lowu,pre[v]);
        }
    }
    return lowu;
}
int LCA(int u,int v)
{
    int cnt = 0;
    while(pre[v] < pre[u]) {
        if(bridge[u]) {
            bridge[u] = 0;
            cnt++;
        }
        u = father[u];
    }
    while(pre[u] < pre[v]) {
        if(bridge[v]) {
            bridge[v] = 0;
            cnt++;
        }
        v = father[v];
    }
    while(u != v) {
        if(bridge[u]) {
            bridge[u] = 0;
            cnt++;
        }
        if(bridge[v]) {
            bridge[v] = 0;
            cnt++;
        }
        u = father[u];
        v = father[v];
    }
    return cnt;
}
void init(int n)
{
    dfs_clock = 0,cnt_bri = 0;
    memset(pre,0,sizeof(pre));
    memset(bridge,0,sizeof(bridge));
    memset(father,0,sizeof(father));
    for(int i = 0; i < n; i++) {
        G[i].clear();
    }
}
int main(void)
{
    int n,m;
    int u,v;
    int Case = 0;
    while(scanf("%d%d",&n,&m) != EOF && (n + m)) {
        init(n);
        for(int i = 0; i < m; i++) {
            scanf("%d%d",&u,&v);
            G[u].push_back(v);
            G[v].push_back(u);
        }

        dfs(1,-1);
        int q;
        scanf("%d",&q);
        printf("Case %d:\n",++Case);
        while(q--) {
            scanf("%d%d",&u,&v);
            int cnt = LCA(u,v);
            cnt_bri -= cnt;
            printf("%d\n",cnt_bri);
        }
    }
    return 0;
}

显式缩点

#include <stdio.h>
#include <string.h>
#include <vector>
#include <queue>
#include <algorithm>

using namespace std;

const int MAXN = 100010; //点数
const int MAXM = 400010; //边数

struct Edge
{
    int to,next;
    bool cut; //是否被标记
}edge[MAXM];
int head[MAXN],tot;
int Low[MAXN],DFN[MAXN],Stack[MAXN],Belong[MAXN]; //数组的值是1~lock
int Index,top;
int block;//边双连通块数
bool Instack[MAXN];
int bridge;
void addedge(int u,int v)
{
    edge[tot].to = v;
    edge[tot].next = head[u];
    edge[tot].cut = false;
    head[u] = tot++;
}
void Tarjan(int u,int pre)
{
    int v;
    Low[u] = DFN[u] = ++Index;
    Stack[top++] = u;
    Instack[u] = true;
    int pre_cnt = 0;
    for(int i = head[u]; i != -1; i = edge[i].next) {
        v = edge[i].to;
        if(v == pre && pre_cnt == 0){
            pre_cnt++;
            continue;
        }
        if(!DFN[v]) {
            Tarjan(v,u);
            Low[u] = min(Low[u],Low[v]);
            if(Low[v] > DFN[u]) {
                bridge++;
                edge[i].cut = true;
                edge[i ^ 1].cut = true;
            }
        }
        else if(Instack[v] && Low[u] > DFN[v]) {
            Low[u] = DFN[v];
        }
    }
    if(Low[u] == DFN[u]) {
        block++;
        do {
            v = Stack[--top];
            Instack[v] = false;
            Belong[v] = block;
        }while(v != u);
    }
}
void init()
{
    tot = 0;
    memset(head,-1,sizeof(head));
}
vector<int> vec[MAXN];
int father[MAXN];
int dep[MAXN];
int a[MAXN];
void lca_bfs(int root)
{
    memset(dep,-1,sizeof(dep));
    dep[root] = 0;
    a[root] = 0;
    father[root] = -1;
    queue<int> q;
    q.push(root);
    while(!q.empty()) {
        int tmp = q.front();
        q.pop();
        for(int i = 0; i < vec[tmp].size(); i++) {
            int v = vec[tmp][i];
            if(dep[v] != -1) continue;
            dep[v] = dep[tmp] + 1;
            a[v] = 1;
            father[v] = tmp;
            q.push(v);
        }
    }
}
int ans;
void lca(int u,int v)
{
    if(dep[u] > dep[v]) swap(u,v);
    while(dep[u] < dep[v]) {
        if(a[v]) {
            ans--;
            a[v] = 0;
        }
        v = father[v];
    }
    while(u != v) {
        if(a[u]) {
            ans--;
            a[u] = 0;
        }
        if(a[v]) {
            ans--;
            a[v] = 0;
        }
        u = father[u];
        v = father[v];
    }
}
void solve(int n)
{
    memset(DFN,0,sizeof(DFN));
    memset(Instack,false,sizeof(Instack));
    Index = top = block = 0;
    Tarjan(1,0);
    for(int i = 1; i <= block; i++) {
        vec[i].clear();
    }
    for(int i = 1; i <= n; i++) {
        for(int j = head[i]; j != -1; j = edge[j].next) {
            int v = edge[j].to;
            if(Belong[i] != Belong[v]) {
                //printf("%d %d\n",i,v);
                vec[Belong[i]].push_back(Belong[v]);
            }
        }
    }
    lca_bfs(1);
    ans = block - 1;
    int Q;
    int u,v;
    scanf("%d",&Q);
    while(Q--) {
        scanf("%d%d",&u,&v);
        lca(Belong[u],Belong[v]);
        printf("%d\n",ans);
    }
    printf("\n");
}
int main(void)
{
    int n,m;
    int u,v;
    int Case = 0;
    while(scanf("%d%d",&n,&m) != EOF && (n + m)) {
        Case++;
        init();
        while(m--) {
            scanf("%d%d",&u,&v);
            addedge(u,v);
            addedge(v,u);
        }
        printf("Case %d:\n",Case);
        solve(n);
    }
    return 0;
}

1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值