POJ3694 Network —— 边双联通分量 + 缩点 + LCA + 并查集
题目链接:https://vjudge.net/problem/POJ-3694
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
题解:
1.利用Tarjan算法,求出每个边双联通分量,并且记录每个点属于哪一个分量。
2.将每一个边双联通分量缩成一个点,最终得到一棵树。而我们想要得到一棵有根树,怎么办?其实在执行Tarjan算法的时候,就已经形成了一个有根树。所以我们只需要在Tarjan算法的基础上,再记录每一个点的父节点以及深度就可以了。
3.每次询问的时候,如果两个点在同一个分量中,那么他们的相连不会减少桥的个数。如果两个点在不同的分量中,那么u->LCA(u,v)和v->LCA(u,v)上路径上的桥,都可以减少,路径上的点都可以缩成一个点,即合并成一个分量。
对于缩点的处理:
方法一:对于一个分量,可以设置一个点为实点,其余的点为虚点。实点即代表着这个分量的所有信息,虚点虽然属于这个分量的点,但是却对他视而不见。我们要做的,就是在这个分量里选择一个点,去代表整个分量。
方法二:同样地,我们也需要为每一个分量选出一个代表,以表示这个分量。与方法一的“视而不见”不同的是,方法二对每一个点都设置了一个归属集合,即表示这个点属于哪一个集合。由于在处理的过程中,一个集合可能又会被另一个集合所包含,所以我们可以利用并查集的路径压缩,很快地找到一个点的最终所属集合。
方法一:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <vector>
#include <queue>
#include <stack>
#include <map>
#include <string>
#include <set>
#define ms(a,b) memset((a),(b),sizeof((a)))
using namespace std;
typedef long long LL;
const double EPS = 1e-;
const int INF = 2e9;
const LL LNF = 2e18;
const int MAXN = 1e5+; struct Edge
{
int to, next;
}edge[MAXN*];
int tot, head[MAXN]; int index, dfn[MAXN], low[MAXN];
int isbridge[MAXN], sum_bridge;
int fa[MAXN], depth[MAXN]; void addedge(int u, int v)
{
edge[tot].to = v;
edge[tot].next = head[u];
head[u] = tot++;
} void Tarjan(int u, int pre)
{
dfn[u] = low[u] = ++index;
depth[u] = depth[pre] + ; //记录深度
fa[u] = pre; //记录父亲结点
for(int i = head[u]; i!=-; i = edge[i].next)
{
int v = edge[i].to;
if(v==pre) continue;
if(!dfn[v])
{
Tarjan(v, u);
low[u] = min(low[u], low[v]);
if(low[v]>dfn[u]) //isbridge[v]表示在树中,以v为儿子结点的边是否为桥
isbridge[v] = , sum_bridge++;
}
else
low[u] = min(low[u], dfn[v]);
}
} void LCA(int u, int v)
{
if(depth[u]<depth[v]) swap(u, v);
while(depth[u]>depth[v]) //深度大的先往上爬。遇到桥,就把它删去。
{
if(isbridge[u]) sum_bridge--, isbridge[u] = ;
u = fa[u];
}
while(u!=v) //当深度一样时,一起爬。遇到桥,就把它删去。
{
if(isbridge[u]) sum_bridge--, isbridge[u] = ;
u = fa[u];
if(isbridge[v]) sum_bridge--, isbridge[v] = ;
v = fa[v];
}
} void init()
{
tot = ;
memset(head, -, sizeof(head)); index = ;
memset(dfn, , sizeof(dfn));
memset(low, , sizeof(low));
memset(isbridge, , sizeof(isbridge)); sum_bridge = ;
} int main()
{
int n, m, kase = ;
while(scanf("%d%d", &n, &m) && (n||m) )
{
init();
for(int i = ; i<=m; i++)
{
int u, v;
scanf("%d%d", &u, &v);
addedge(u, v);
addedge(v, u);
} depth[] = ;
Tarjan(, );
int q, a, b;
scanf("%d", &q);
printf("Case %d:\n", ++kase);
while(q--)
{
scanf("%d%d", &a, &b);
LCA(a, b);
printf("%d\n", sum_bridge);
}
printf("\n");
}
}
方法二:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <vector>
#include <queue>
#include <stack>
#include <map>
#include <string>
#include <set>
#define ms(a,b) memset((a),(b),sizeof((a)))
using namespace std;
typedef long long LL;
const double EPS = 1e-;
const int INF = 2e9;
const LL LNF = 2e18;
const int MAXN = 1e6+; struct Edge
{
int to, next;
}edge[MAXN], edge0[MAXN]; //edge为初始图, edge0为重建图
int tot, head[MAXN], tot0, head0[MAXN]; int index, dfn[MAXN], low[MAXN];
int top, Stack[MAXN], instack[MAXN];
int belong[MAXN];
int fa[MAXN], depth[MAXN]; //fa用于重建图时记录当前节点的父亲节点,depth记录当前节点的深度
int sum_bridge; //找到x最终所属的结合
int find(int x) { return belong[x]==x?x:belong[x]=find(belong[x]); } void addedge(int u, int v, Edge edge[], int head[], int &tot)
{
edge[tot].to = v;
edge[tot].next = head[u];
head[u] = tot++;
} void Tarjan(int u, int pre)
{
dfn[u] = low[u] = ++index;
Stack[top++] = u;
instack[u] = true;
for(int i = head[u]; i!=-; i = edge[i].next)
{
int v = edge[i].to;
if(v==pre) continue;
if(!dfn[v])
{
Tarjan(v, u);
low[u] = min(low[u], low[v]);
if(low[v]>dfn[u]) sum_bridge++;
}
else if(instack[v])
low[u] = min(low[u], dfn[v]);
} if(dfn[u]==low[u])
{
int v;
do
{
v = Stack[--top];
instack[v] = false;
belong[v] = u; //把集合的编号设为联通分量的第一个点
}while(v!=u);
}
} void build(int u, int pre)
{
fa[u] = pre; //记录父亲节点
depth[u] = depth[pre] + ; //记录深度
for(int i = head0[u]; i!=-; i=edge0[i].next)
if(edge0[i].to!=pre) //防止往回走
build(edge0[i].to, u);
} int LCA(int u, int v) //左一步右一步地找LCA
{
if(u==v) return u; //因为两个结点一定有LCA, 所以一定有u==v的时候 //可能爬一步就爬了几个深度,因为中间的结点已经往上缩点了
if(depth[u]<depth[v]) swap(u, v); //深度大的往上爬
sum_bridge--;
int lca = LCA(find(fa[u]), v);
return belong[u] = lca; //找到了LCA,在沿路返回的时候把当前节点的所属集合置为LCA的所属集合
} void init()
{
tot = tot0 = ;
memset(head, -, sizeof(head));
memset(head0, -, sizeof(head0)); index = top = ;
memset(dfn, , sizeof(dfn));
memset(low, , sizeof(low));
memset(instack, , sizeof(instack)); sum_bridge = ;
} int main()
{
int n, m, kase = ;
while(scanf("%d%d", &n, &m) && (n||m) )
{
init();
for(int i = ; i<=m; i++)
{
int u, v;
scanf("%d%d", &u, &v);
addedge(u, v, edge, head, tot);
addedge(v, u, edge, head, tot);
} Tarjan(, );
for(int u = ; u<=n; u++) //重建建图
for(int i = head[u]; i!=-; i = edge[i].next)
{
int tmpu = find(u);
int tmpv = find(edge[i].to);
if(tmpu!=tmpv)
addedge(tmpu, tmpv, edge0, head0, tot0);
} depth[find()] = ;
build(find(), find()); //把无根树转为有根树 int q, a, b;
scanf("%d", &q);
printf("Case %d:\n", ++kase);
while(q--)
{
scanf("%d%d", &a, &b);
LCA(find(a), find(b));
printf("%d\n", sum_bridge);
}
printf("\n");
}
}
POJ3694 Network —— 边双联通分量 + 缩点 + LCA + 并查集的更多相关文章
- POJ 3694Network(Tarjan边双联通分量 + 缩点 + LCA并查集维护)
[题意]: 有N个结点M条边的图,有Q次操作,每次操作在点x, y之间加一条边,加完E(x, y)后还有几个桥(割边),每次操作会累积,影响下一次操作. [思路]: 先用Tarjan求出一开始总的桥的 ...
- HDU5409---CRB and Graph 2015多校 双联通分量缩点
题意:一个联通的无向图, 对于每一条边, 若删除该边后存在两点不可达,则输出这两个点, 如果存在多个则输出第一个点尽可能大,第二个点尽可能小的. 不存在输出0 0 首先 若删除某一条边后存在多个联通分 ...
- POJ3177 Redundant Paths —— 边双联通分量 + 缩点
题目链接:http://poj.org/problem?id=3177 Redundant Paths Time Limit: 1000MS Memory Limit: 65536K Total ...
- POJ3694 Network 边双缩点+LCA+并查集
辣鸡错误:把dfs和ldfs搞混...QAQ 题意:给定一个无向图,然后查询q次,求每次查询就在图上增加一条边,求剩余割边的个数. 先把边双缩点,然后预处理出LCA的倍增数组: 然后加边时,从u往上跳 ...
- POJ 3694 (tarjan缩点+LCA+并查集)
好久没写过这么长的代码了,题解东哥讲了那么多,并查集优化还是很厉害的,赶快做做前几天碰到的相似的题. #include <iostream> #include <algorithm& ...
- 边双联通分量缩点+树的直径——cf1000E
题意理解了就很好做 题意:给一张无向图,任意取两个点s,t,s->t的路径上必经边数量为k 求这样的s,t,使得k最大 #include<bits/stdc++.h> #define ...
- Poj 3694 Network (连通图缩点+LCA+并查集)
题目链接: Poj 3694 Network 题目描述: 给出一个无向连通图,加入一系列边指定的后,问还剩下多少个桥? 解题思路: 先求出图的双连通分支,然后缩点重新建图,加入一个指定的边后,求出这条 ...
- 【UVA10972】RevolC FaeLoN (求边双联通分量)
题意: 给你一个无向图,要求把所有无向边改成有向边,并且添加最少的有向边,使得新的有向图强联通. 分析: 这题的解法还是很好想的.先用边双联通分量缩点,然后找新图中入度为0和为1的点,入度为0则ans ...
- 洛谷P2860 [USACO06JAN]冗余路径Redundant Paths(tarjan求边双联通分量)
题目描述 In order to get from one of the F (1 <= F <= 5,000) grazing fields (which are numbered 1. ...
随机推荐
- 404 Not Found 由来
404 NOT FOUND! 抱歉,沒有找到您需要的文章!! 什么是 404 Not Found 404页面是网站必备的一个页面,它承载着用户体验与SEO优化的重任.404页面通常为用户访问了网站上不 ...
- luogu1447 [NOI2010]能量采集
考虑暴力,答案显然是 \(\sum_{i=1}^n\sum_{j=1}^m(2(\gcd(i,j)-1)+1)=\sum_{i=1}^n\sum_{j=1}^m(2\gcd(i,j)-1)\). 考虑 ...
- C. The Smallest String Concatenation-C++sort排序~~
C. The Smallest String Concatenation time limit per test 3 seconds memory limit per test 256 megabyt ...
- mysql 获取所有的数据库名字
mysql 获取所有的数据库名字 一.如果使用的是mysqli: $con = @mysqli_connect("localhost", "root", &qu ...
- python(4)- 字符编码
一 什么是编码? 基本概念很简单.首先,我们从一段信息即消息说起,消息以人类可以理解.易懂的表示存在.我打算将这种表示称为“明文”(plain text).对于说英语的人,纸张上打印的或屏幕上显示的英 ...
- 编译.net .net Core程序 代码,仅做备份
//创建一个ProcessStartInfo对象 使用系统shell 指定命令和参数 设置标准输出 //编译.net core项目 var psi = new ProcessStartInfo(&qu ...
- EXTJS中整合tinymce的富文本编辑器,添加上传图片功能
提供部分代码.Ext.create('Ext.window.Window', { id: 'wind', title: 'CRUD窗口', modal: true, height: 800, widt ...
- MySql基本数据类型(转)
说明:通俗的理解:1字节的8位,即1byte=8bit,而这个1byte叫做长度范围,范围的算法是使用bit去求,比如8bit的长度范围是2的8次方,但是在数据库中的类型上是有区分有符号和无符号的,默 ...
- eclipse提速01 - 禁用不常用的eclipse启动插件
会不断更新,需要的收藏 禁用不常用的eclipse启动插件(当然如果不需要可以卸载)
- zerorpc使用时报错:No handlers could be found for logger "zerorpc.channel"
问题如题:安装方法参考 http://www.cnblogs.com/shengulong/p/7887586.html ,安装完后,使用时出现如题的错误 解决办法: 1.zerorpc本身依赖很多三 ...