【题意】:

有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. 8.18 NOIP模拟测试25(B) 字符串+乌鸦喝水+所驼门王的宝藏

    T1 字符串 卡特兰数 设1为向(1,1)走,0为向(1,-1)走,限制就是不能超过$y=0$这条线,题意转化为从(0,0)出发,走到(n+m,n-m)且不越过$y=0$,然后就裸的卡特兰数,$ans ...

  2. Web协议详解与抓包实战:HTTP1协议-HTTP 响应行(3)

    一.HTTP 响应行 二.响应码分类:1xx 三.响应码分类: 2xx 1. 201 Created: 有新资源在服务器端被成功创建 2.207 Multi-Status:RFC4918 ,在 WEB ...

  3. [LeetCode] 103. Binary Tree Zigzag Level Order Traversal 二叉树的之字形层序遍历

    Given a binary tree, return the zigzag level order traversal of its nodes' values. (ie, from left to ...

  4. 实现100以内的素数输出(Python与C++对比)

    今天从链接http://www.2cto.com/kf/201302/187699.html中看到了Python实现100以内的素数输出的算法,颇受感触.尤其是被其中的Python的列表生成器的使用方 ...

  5. OAuth2.0 自我领悟

    grant_type 授权模式 authorization_code 标准的Server授权模式,授权码模式 password 基于用户密码的授权模式,用户密码模式 client_credential ...

  6. 基于Django的Rest Framework框架的序列化组件

    本文目录 一 Django自带序列化组件 二 rest-framework序列化之Serializer 三 rest-framework序列化之ModelSerializer 四 生成hypermed ...

  7. (十四)golang--函数和包

    1.怎么定义函数? func (形参列表) 返回值列表{ 执行操作 return } 2.什么是包? 包的本质就是一个文件夹,存放程序文件 三大作用: 区分相同的名字的函数.变量等标识符: 当程序文件 ...

  8. SpringBoot之Swagger2文档生成

    SpringBoot之Swagger2文档生成 1.Swagger2介绍 编写和维护接口文档是每个程序员的职责,前面我们已经写好的接口现在需要提供一份文档,这样才能方便调用者使用.考虑到编写接口文档是 ...

  9. SpringBoot整合mybatis及注意事项

    SpringBoot整合mybatis及注意事项 主要步骤 添加依赖 mybatis 在配置文件中配置数据源信息 编写pojo mapper接口 mapeer映射文件 手动配置mybatis的包扫描 ...

  10. HyperLogLog算法分析及其应用

    HyperLogLog 算法的原理讲解以及 Redis 是如何应用它的 探索HyperLogLog算法(含Java实现) 神奇的HyperLogLog算法 Sketch of the Day: Hyp ...