【题意】:

有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. 「总结」插头$dp$

    集中做完了插头$dp$ 写一下题解. 一开始学的时候还是挺蒙的. 不过后来站在轮廓线$dp$的角度上来看就简单多了. 其实就是一种联通性$dp$,只不过情况比较多而已了. 本来转移方式有两种.逐行和逐 ...

  2. 【2019.7.16 NOIP模拟赛 T1】洗牌(shuffle)(找环)

    找环 考虑每次洗牌其实是一次置换的过程,而这样必然就会有循环出现. 因此我们直接通过枚举找出每一个循环,询问时只要找到环上对应的位置就可以了. 貌似比我比赛时被卡成\(30\)分的倍增简单多了? 代码 ...

  3. [LeetCode] 29. Divide Two Integers 两数相除

    Given two integers dividend and divisor, divide two integers without using multiplication, division ...

  4. Java连载15-boolean类型&类型转换&++运算符

    一.boolean类型 1.说明: (1)在java语言中,boolean类型只有两个值:true.false,没有其他的值.在C语言中,是有0代表false和1代表true的 (2)在底层存储的时候 ...

  5. docker命令集合

    #docker安装yum -y install docker-iodocker --version #启动Docker进程systemctl start dockersystemctl status ...

  6. git 版本(commit) 回退 -- 使用git reset 指令

    刚刚提交了三个commit, git reflog显示如下: 最后一个commit在文件末尾加了一行:v3,以此类推: 下面,使用git reset --hard commitID来进行commit回 ...

  7. Java ReentrantLock中tryLock与lock的区别(非公平锁与公平锁)

    设置同步状态,利用CAS操作. // CAS操作:如果当前状态值等于期望值,则自动将同步状态设置为给定的更新值 protected final boolean compareAndSetState(i ...

  8. Spring Cloud灰度发布之Nepxion Discovery

    <蓝绿部署.红黑部署.AB测试.灰度发布.金丝雀发布.滚动发布的概念与区别> 最近公司项目在做架构升级,升级为 Spring Cloud,我们希望能够做到服务的灰度发布,根据访问量逐渐切换 ...

  9. ​ Markdown

    Cygwin ​ Markdown是一种可以使用普通文本编辑器编写的标记语言,通过简单的标记语法,它可以使普通文本内容具有一定的格式. ​ Markdown具有一系列衍生版本,用于扩展Markdown ...

  10. HTML+CSS学习笔记整理

    一.标签语义化(重点): 1.可以方便代码的阅读和维护 2.同时让网络爬虫更好的解析从而更好的分析其内容 3.更好的优化引擎 如何做到标签语义化:个人理解是,首先,网页的HTML主要作用在网页的结构上 ...