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;
}

猜你喜欢

转载自blog.csdn.net/gyh0730/article/details/81051357