POJ 3694Network(Tarjan边双联通分量 + 缩点 + LCA并查集维护)
【题意】:
有N个结点M条边的图,有Q次操作,每次操作在点x, y之间加一条边,加完E(x, y)后还有几个桥(割边),每次操作会累积,影响下一次操作。
【思路】:
先用Tarjan求出一开始总的桥的数量,然后求边双联通分量并记录每个结点v所属的连通分量号c[v],之后进行缩点,将每个双联通分量作为都缩成一个新点,如果新点之间可以连边就连边
(能不能连边取决于原图,我就不多bb辽,XD),形成新图。
对于每次询问x, y,判断c[x]!=c[y],然后从c[x]和c[y]分别向上寻找父结点,找到LCA,对c[x]寻找时经过的边数+对c[y]寻找时经过的边数==应该减去的桥数。
考虑到每次操作的累加性,已经在之前操作中经过的边已经不是桥,不能在后续操作中再进行统计,所以使用并查集,每当c[x],c[y]找到lca时,就将pre[c[x]] = pre[c[y]] = lca。
求LCA时涉及几乎涉及到每条边,就不使用倍增LCA(主要是我不会??),而是用定义的方法。
下面上代码,第一个代码是求了桥,然后再进行求强联通分量,再加边。 第二个是先求强联通分量(当然是有限制的,不然因为整个图就是联通的,肯定就一个SCC了),再加边。
个人倾向于第二种袄,而且速度快

#include <iostream>
#include <cstring>
#include <cstdio>
#include <map>
#include <map>
using namespace std; const int maxn = 1e6 + ;
const int maxm = maxn<<;
struct edge{
int to, next;
} ed[maxm<<];
int n, m, q;
int head[maxn], tot;
int dfn[maxn], low[maxn], num, ans, c[maxn], dcc;
int hc[maxn], vc[maxm<<], nc[maxm<<], tc;
int pre[maxn], fa[maxn], dep[maxn], pass;
bool brige[maxn], vis[maxn];
inline void init(){
memset( head, -, sizeof(head) );
memset( dfn, , sizeof(dfn) );
memset( brige, , sizeof(brige) );
memset( c, , sizeof(c) );
memset( vis, , sizeof(vis) );
tot = ;
} inline void add( int u, int v ){
ed[++tot].to = v; ed[tot].next = head[u]; head[u] = tot;
ed[++tot].to = u; ed[tot].next = head[v]; head[v] = tot;
} inline int min( int a, int b ){
return a<b ? a:b;
} inline void tarjan( int x, int in_edge ){
dfn[x] = low[x] = ++num;
for( int i=head[x]; i!=-; i=ed[i].next ){
int y = ed[i].to;
if(!dfn[y]){
tarjan(y, i);
low[x] = min(low[x], low[y]);
if( dfn[x]<low[y] ){
brige[i] = brige[i^] = ;
ans ++;
}
}else if( i!=(in_edge^) ) low[x] = min(low[x], dfn[y]);
}
} inline void add_dcc( int u, int v ){
vc[++tc] = v;
nc[tc] = hc[u];
hc[u] = tc;
} inline void dfs_dcc( int x ){
c[x] = dcc;
for( int i=head[x]; i!=-; i=ed[i].next ){
int y = ed[i].to;
if( brige[i] || c[y] ) continue;
dfs_dcc(y);
}
} inline int find( int x ){
return pre[x]==x ? x:pre[x] = find(pre[x]);
} inline void dfs_lca( int x ){ //结点分层
pre[x] = x;
for( int i=hc[x]; i!=-; i=nc[i] ){
int y = vc[i];
if( y!=fa[x] ){
fa[y] = x;
dep[y] = dep[x] + ;
dfs_lca(y);
}
}
} inline void LCA( int x, int y ){
pass = ;
x = find(x); y = find(y); //直接将x,y向上寻找的路径中已经计算过得边略过
while( dep[y]!=dep[x] ){
if( dep[y]>dep[x] ){
int f = find(fa[y]); //当pre[y] == y时f是y的父亲,当pre[y]在y上方时,f就是相当于爷爷或者更高的祖辈
y = pre[y] = f; //不能写成pre[y] = y = f这样y先被赋值,pre[y]则改变的是赋值后的y即pre[f]被改变
pass ++;
}else{
int f = find(fa[x]);
x = pre[x] = f;
pass++;
}
}
while( find(x)!=find(y) ){
pre[x] = find(fa[x]);
pre[y] = find(fa[y]);
x = pre[x]; y = pre[y];
pass += ;
}
} int main(){
// freopen("in.txt", "r", stdin);
int kase = ;
while( ~scanf("%d%d", &n, &m), n||m ){
init();
for( int i=; i<m; i++ ){
int u, v;
scanf("%d%d", &u, &v);
add(u, v);
}
ans = dcc = num = ;
tarjan(, );
for( int i=; i<=n; i++ ) if( !c[i] ) ++dcc, dfs_dcc(i);
memset( hc, -, sizeof(hc) );
tc = ;
//不要使用map作为标记,遍历边进行新图的加边操作,map会TLE
for( int u=; u<=n; u++ ){
for( int i=head[u]; i!=-; i=ed[i].next ){
int v = ed[i].to;
if( c[u]==c[v] ) continue;
add_dcc(c[u], c[v]);
}
}
ans = tc>>;
dep[] = ;
fa[] = ;
dfs_lca();
scanf("%d", &q);
printf("Case %d:\n", kase++);
while( q-- ){
int x, y;
scanf("%d%d", &x, &y);
if( c[x]!=c[y] ){
LCA(c[x], c[y]);
ans -= pass;
}
printf("%d\n", ans);
}
puts("");
} return ;
}
先求桥,再求边双联通,再连边进行LCA
#include <iostream>
#include <cstring>
#include <cstdio>
#include <map>
#include <map>
using namespace std; const int maxn = 1e6 + ;
const int maxm = maxn<<;
struct edge{
int to, next;
} ed[maxm<<];
int n, m, q;
int head[maxn], tot, st[maxn];
int dfn[maxn], low[maxn], num, ans, c[maxn], dcc;
int hc[maxn], vc[maxm<<], nc[maxm<<], tc;
int pre[maxn], fa[maxn], dep[maxn], pass;
bool ins[maxn], vis[maxn];
inline void init(){
memset( head, -, sizeof(head) );
memset( dfn, , sizeof(dfn) );
memset( c, , sizeof(c) );
memset( vis, , sizeof(vis) );
tot = ;
} inline void add( int u, int v ){
ed[++tot].to = v; ed[tot].next = head[u]; head[u] = tot;
ed[++tot].to = u; ed[tot].next = head[v]; head[v] = tot;
} inline int min( int a, int b ){
return a<b ? a:b;
} inline void tarjan( int x, int in_edge ){
dfn[x] = low[x] = ++num;
ins[x] = ;
st[++st[]] = x;
for( int i=head[x]; i!=-; i=ed[i].next ){
int y = ed[i].to;
if( i==(in_edge^) ) continue;
if(!dfn[y]){
tarjan(y, i);
low[x] = min(low[x], low[y]);
}else if( ins[y] ) low[x] = min(low[x], dfn[y]);
}
if( dfn[x]==low[x] ){
dcc ++;
int p;
do{
p = st[st[]--];
c[p] = dcc;
ins[p] = ;
}while( p!=x );
}
} inline void add_dcc( int u, int v ){
vc[++tc] = v;
nc[tc] = hc[u];
hc[u] = tc;
} inline int find( int x ){
return pre[x]==x ? x:pre[x] = find(pre[x]);
} inline void dfs_lca( int x ){
pre[x] = x;
for( int i=hc[x]; i!=-; i=nc[i] ){
int y = vc[i];
if( y!=fa[x] ){
fa[y] = x;
dep[y] = dep[x] + ;
dfs_lca(y);
}
}
} inline void LCA( int x, int y ){
pass = ;
x = find(x); y = find(y);
while( dep[y]!=dep[x] ){
if( dep[y]>dep[x] ){
int f = find(fa[y]); //当pre[y] == y时f是y的父亲,当pre[y]在y上方时,f就是相当于爷爷或者更高的祖辈
y = pre[y] = f; //不能写成pre[y] = y = f这样y先被赋值,pre[y]则改变的是赋值后的y即pre[f]被改变
pass ++;
}else{
int f = find(fa[x]);
x = pre[x] = f;
pass++;
}
}
while( find(x)!=find(y) ){
pre[x] = find(fa[x]);
pre[y] = find(fa[y]);
x = pre[x]; y = pre[y];
pass += ;
}
} int main(){
// freopen("in.txt", "r", stdin);
int kase = ;
while( ~scanf("%d%d", &n, &m), n||m ){
init();
for( int i=; i<m; i++ ){
int u, v;
scanf("%d%d", &u, &v);
add(u, v);
}
ans = dcc = num = ;
for( int i=; i<=n; i++ ) if(!dfn[i]) tarjan(, );
memset( hc, -, sizeof(hc) );
tc = ;
for( int u=; u<=n; u++ ){
for( int i=head[u]; ~i; i=ed[i].next ){
int v = ed[i].to;
if( c[u]==c[v] ) continue;
add_dcc(c[u], c[v]);
}
}
ans = tc>>;
dep[] = ;
fa[] = ;
dfs_lca();
scanf("%d", &q);
printf("Case %d:\n", kase++);
while( q-- ){
int x, y;
scanf("%d%d", &x, &y);
if( c[x]!=c[y] ){
LCA(c[x], c[y]);
ans -= pass;
}
printf("%d\n", ans);
}
puts("");
} return ;
}
求强联通分量,再加边,进行LCA
POJ 3694Network(Tarjan边双联通分量 + 缩点 + LCA并查集维护)的更多相关文章
- POJ3694 Network —— 边双联通分量 + 缩点 + LCA + 并查集
题目链接:https://vjudge.net/problem/POJ-3694 A network administrator manages a large network. The networ ...
- 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 ...
- POJ 3177 Redundant Paths 双联通分量 割边
http://poj.org/problem?id=3177 这个妹妹我大概也曾见过的~~~我似乎还没写过双联通分量的blog,真是智障. 最少需要添多少条边才能使这个图没有割边. 边双缩点后图变成一 ...
- POJ2942 Knights of the Round Table【Tarjan点双联通分量】【二分图染色】【补图】
LINK 题目大意 有一群人,其中有一些人之间有矛盾,现在要求选出一些人形成一个环,这个环要满足如下条件: 1.人数大于1 2.总人数是奇数 3.有矛盾的人不能相邻 问有多少人不能和任何人形成任何的环 ...
- [J]computer network tarjan边双联通分量+树的直径
https://odzkskevi.qnssl.com/b660f16d70db1969261cd8b11235ec99?v=1537580031 [2012-2013 ACM Central Reg ...
- BZOJ 压力 tarjan 点双联通分量+树上差分+圆方树
题意 如今,路由器和交换机构建起了互联网的骨架.处在互联网的骨干位置的核心路由器典型的要处理100Gbit/s的网络流量. 他们每天都生活在巨大的压力之下.小强建立了一个模型.这世界上有N个网络设备, ...
- Tarjan求强联通分量+缩点
提到Tarjan算法就不得不提一提Tarjan这位老人家 Robert Tarjan,计算机科学家,以LCA.强连通分量等算法闻名.他拥有丰富的商业工作经验,1985年开始任教于普林斯顿大学.Tarj ...
- POJ3177 Redundant Paths【tarjan边双联通分量】
LINK 题目大意 给你一个有重边的无向图图,问你最少连接多少条边可以使得整个图双联通 思路 就是个边双的模板 注意判重边的时候只对父亲节点需要考虑 你就dfs的时候记录一下出现了多少条连向父亲的边就 ...
随机推荐
- CF1175E Minimal Segment Cover
题目链接 题意 给出n条线段.m次询问,每次询问给出一个区间\([l,r]\)问最少需要多少条线段才能覆盖区间\([l,r]\). 所有坐标\(\le 5\times 10^5\).\(n,m\le ...
- Log-Structured Merge Tree (LSM Tree)
一种树,适合于写多读少的场景.主要是利用了延迟更新.批量写.顺序写磁盘(磁盘sequence access比random access快). 背景 回顾数据存储的两个“极端”发展方向 加快读:加索引( ...
- @Value不能给静态变量直接赋值问题
1. 平时用的时候,直接在变量头上加上@Value就能到值(其中path.url是配置文件properties的.). @Value("${path.url}") private ...
- IDEA 最新破解方法,2019 版本已支持(持续更新,更新时间2019-05-14)
一.安装进入官网下载适合自己系统的版本,下载地址:https://www.jetbrains.com/idea/download/#section=windows 2. 安装中选择适合本机的64位 ...
- Java一个简单的重试工具包
在接口调用中由于各种原因,可能会重置失败的任务,使用Guava-Retrying可以方便的实现重试功能. 首先,需要引用Guava-Retrying的包 <dependency> < ...
- JAVA Api 调用Hbase报错锦集
1. 报错 java.lang.NoClassDefFoundError: org/apache/hadoop/hbase/protobuf/generated/MasterProtos$Master ...
- 【linux】CentOS 查看系统时间,修改时区
===============CentOS 7.6================ 1.查看系统时间 date 查看当前系统时间以及时区结果是: Mon Jul 8 09:23:31 UTC 2019 ...
- jQuery实现C#CheckBoxList模糊搜索
前言 最近开发的一套系统中需要对商品进行管理,在选择商品时,要分别从品牌.型号.商品三个类别分别选择对应的选项才能找到需要的商品,三者的关系为:品牌包含型号,型号包含商品,因此使用了三个不同的 asp ...
- ASP.NET MVC 过滤、异常过滤器
记录下过滤器的学习—_— APS.NET MVC中的每一个请求,都会分配给相应的控制器和对应的行为方法去处理,而在这些处理的前后如果想再加一些额外的逻辑处理,这样会造成大量代码的重复使用,这不是我们希 ...
- C#中的System.Type和System.RuntimeType之间的区别
string str = string.Empty; Type strType = str.GetType(); Type strTypeType = strType.GetType(); strTy ...