【题意】:

有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并查集维护)的更多相关文章

  1. POJ3694 Network —— 边双联通分量 + 缩点 + LCA + 并查集

    题目链接:https://vjudge.net/problem/POJ-3694 A network administrator manages a large network. The networ ...

  2. HDU5409---CRB and Graph 2015多校 双联通分量缩点

    题意:一个联通的无向图, 对于每一条边, 若删除该边后存在两点不可达,则输出这两个点, 如果存在多个则输出第一个点尽可能大,第二个点尽可能小的. 不存在输出0 0 首先 若删除某一条边后存在多个联通分 ...

  3. POJ3177 Redundant Paths —— 边双联通分量 + 缩点

    题目链接:http://poj.org/problem?id=3177 Redundant Paths Time Limit: 1000MS   Memory Limit: 65536K Total ...

  4. POJ 3177 Redundant Paths 双联通分量 割边

    http://poj.org/problem?id=3177 这个妹妹我大概也曾见过的~~~我似乎还没写过双联通分量的blog,真是智障. 最少需要添多少条边才能使这个图没有割边. 边双缩点后图变成一 ...

  5. POJ2942 Knights of the Round Table【Tarjan点双联通分量】【二分图染色】【补图】

    LINK 题目大意 有一群人,其中有一些人之间有矛盾,现在要求选出一些人形成一个环,这个环要满足如下条件: 1.人数大于1 2.总人数是奇数 3.有矛盾的人不能相邻 问有多少人不能和任何人形成任何的环 ...

  6. [J]computer network tarjan边双联通分量+树的直径

    https://odzkskevi.qnssl.com/b660f16d70db1969261cd8b11235ec99?v=1537580031 [2012-2013 ACM Central Reg ...

  7. BZOJ 压力 tarjan 点双联通分量+树上差分+圆方树

    题意 如今,路由器和交换机构建起了互联网的骨架.处在互联网的骨干位置的核心路由器典型的要处理100Gbit/s的网络流量. 他们每天都生活在巨大的压力之下.小强建立了一个模型.这世界上有N个网络设备, ...

  8. Tarjan求强联通分量+缩点

    提到Tarjan算法就不得不提一提Tarjan这位老人家 Robert Tarjan,计算机科学家,以LCA.强连通分量等算法闻名.他拥有丰富的商业工作经验,1985年开始任教于普林斯顿大学.Tarj ...

  9. POJ3177 Redundant Paths【tarjan边双联通分量】

    LINK 题目大意 给你一个有重边的无向图图,问你最少连接多少条边可以使得整个图双联通 思路 就是个边双的模板 注意判重边的时候只对父亲节点需要考虑 你就dfs的时候记录一下出现了多少条连向父亲的边就 ...

随机推荐

  1. shell脚本特殊变量($0、$1、$2、 $?、 $# 、$@、 $*)

    $0        Shell本身的文件名$1-$n 添加到Shell的各参数值.$1是第1参数.$2是第2参数…$$        Shell本身的PID(ProcessID) $!         ...

  2. xshell 与服务器断开连接后 服务停止500internal error

    看某教程用uwsgi +nginx运行django项目,但是xshell关掉之后服务会停止. 大佬一席话,胜趟十天坑. 把supervisor配置好之后正常运行. 如何配置?百度啊! 附录一个好的教程 ...

  3. Sentinel: 使用注解限流

    在前面我们对Sentinel做了一个详细的介绍,可以手动的通过Sentinel提供的SphU类来保护资源.这种做法不好的地方在于每个需要限制的地方都得写代码,从 0.1.1 版本开始,Sentinel ...

  4. Python-lambda使用

    什么是lambda 匿名函数,不需要命名的函数: 语法 lambda 参数 : 返回值 g = lambda x: 2*x+1 g(2) >5

  5. 多线程下的HashMap竟然绕环了

    导读:早就听说过HashMap不是线程安全的,在多线程情况下可能会出问题,自己一直是一知半解,正好五一有时间就抽时间来研究一下. 关键词:线程安全,HashMap 直接上图 总结 看过的知识点不一定属 ...

  6. kafka的安装和初步使用

    简介 最近开发的项目中,kafka用的比较多,为了方便梳理,从今天起准备记录一些关于kafka的文章,首先,当然是如何安装kafka了. Apache Kafka是分布式发布-订阅消息系统. Apac ...

  7. SpringBoot之CommandLineRunner接口和ApplicationRunner接口

    我们在开发中可能会有这样的情景.需要在容器启动的时候执行一些内容.比如读取配置文件,数据库连接之类的.SpringBoot给我们提供了两个接口来帮助我们实现这种需求.这两个接口分别为CommandLi ...

  8. Linux下Maven私服Nexus3.x环境构建操作记录

    原文地址:https://blog.csdn.net/liupeifeng3514/article/details/79553747 私服介绍 私服是指私有服务器,是架设在局域网的一种特殊的远程仓库, ...

  9. pytorch_05_神经网络

    神经网络 一些神经元的输出会变成另外一些神经元的输入,一般以层来组织,最常见的是全连接神经网络,其中两个相邻层中每一个层的所有神经元与另一个层的所有神经元相连,每个层内部的神经元不相连. 一般的,N层 ...

  10. pandas的使用(4)

    pandas的使用(4)--文件读取和保存