【题意】:

有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. python读写、创建文件、文件夹等等

    python中对文件.文件夹(文件操作函数)的操作需要涉及到os模块和shutil模块. 得到当前工作目录,即当前Python脚本工作的目录路径: os.getcwd() 返回指定目录下的所有文件和目 ...

  2. Linux性能优化实战学习笔记:第五讲

    一.什么是CPU的使用率 1.你最常用什么指标来描述系统的CPU性能? 我想你的答案,可能不是平均负载,也不是CPU上下文切换,而是另一个更直观的指标CPU使用率 CPU使用率到底是怎么算出来的吗? ...

  3. [LeetCode] 881. Boats to Save People 渡人的船

    The i-th person has weight people[i], and each boat can carry a maximum weight of limit. Each boat c ...

  4. Office 2016正式版/2019预览版 使用注意

    改写状态已经被隐藏 右击Word状态栏可以看到改写是否打开

  5. 用 cgroups 管理 cpu 资源

    转自:http://xiezhenye.com/2013/10/用-cgroups-管理-cpu-资源.html 这回说说怎样通过 cgroups 来管理 cpu 资源.先说控制进程的 cpu 使用. ...

  6. <a href="javascript:;">的用法说明

    <a href="javascript:;">的用法说明 1.标签的 href 属性用于指定超链接目标的 URL,href 属性的值可以是任何有效文档的相对或绝对 UR ...

  7. ffmpeg Operation not permitted 报错的解决过程记录

    问题重现 由于视频的录制过程出现了一些小问题,需要重新将视频文件切割和合并,找了几个视频编辑软件来做这个事情,最终的结果都不是特别满意,当时已经挺晚的了,本来打算上床睡觉第二天再去想辙,从椅子上起身的 ...

  8. sync 异步编程

    using System; using System.Net; using System.Threading; using System.Threading.Tasks; namespace Cons ...

  9. [数据结构 - 第8章] 查找之哈希表(C语言实现)

    首先是需要定义一个哈希表的结构以及一些相关的常数.其中 HashTable 就是哈希表结构.结构当中的 elem 为一个动态数组. #define HASHSIZE 12 // 定义哈希表长为数组的长 ...

  10. mysql操作(精简版)

    一.数据库操作(建库.删库) 1.查看数据库:show databases; 2.创建数据库:DROP DATABASE 数据库名; 3.删除数据库:CREATE DATABASE 数据库名; 4.使 ...