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. ...
随机推荐
- 为应用指定多个struts配置文件
在大部分应用里,随着应用规模的增加,系统中Action的数量也会大量增加,导致struts.xml配置文件变得非常臃肿.为了避免struts.xml文件过于庞大.臃肿,提高struts.xml文件的可 ...
- POJ3621 Sightseeing Cows【最短路】
题目大意:在一个无向图里找一个环,是的点权和除以边权和最大 思路:UVA11090姊妹题 事实上当这题点权和都为1时就是上一题TUT #include <stdio.h> #include ...
- 【dp】HDU 1421 搬寝室
http://acm.hdu.edu.cn/showproblem.php?pid=1421 [题意] 给定n个数,要从n个数中选择k个二元组{x,y},最小化sum{(x-y)^2} 2<=2 ...
- 【BZOJ1008】越狱(排列组合计数,容斥原理)
题意: 思路: #include<cstdio> #include<cstdlib> #include<iostream> #include<algorith ...
- 【ZJOI2017 Round1练习&BZOJ4767】D1T3 两双手(排列组合,DP)
题意: 100%的数据:|Ax|,|Ay|,|Bx|,|By| <= 500, 0 <= n,Ex,Ey <= 500 思路:听说这是一道原题 只能往右或者下走一步且有禁止点的简化版 ...
- 210 Course ScheduleII
/* * 210 Course ScheduleII * 2016-6-9 by Mingyang * http://www.jyuan92.com/blog/leetcode-course-sche ...
- 个人网站开发***云服务器+Linux+域名***
作为一个改变世界的程序猿,我们不应该只会埋头写程序修bug还得会点别的, 当然如果要是自己搞个网站玩玩,既可以锻炼技术,没事也可以和圈外的朋友吹吹 牛.因为水平有限,就弄一些最基础的看看喽,不喜勿喷. ...
- 框架-弹出选择框(Jquery传递Json数组)
给一个button按钮,执行方法 Json传值$("body").on("click", "#btnsure", function() { ...
- Samba简单配置--匿名用户共享资料可读可写的实现
http://e-mailwu.blog.163.com/blog/static/65104036200931893921923/ http://www.cnblogs.com/god_like_do ...
- Python遍历路径下文件并转换成UTF-8编码
http://www.cnblogs.com/wuyuegb2312/archive/2013/01/11/2856772.html 开始学Python,这篇文章来自于应用需求. os.walk很方便 ...