【题意】:

有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 + 5;
const int maxm = maxn<<1;
struct edge{int to, next;
} ed[maxm<<1];
int n, m, q;
int head[maxn], tot;
int dfn[maxn], low[maxn], num, ans, c[maxn], dcc;
int hc[maxn], vc[maxm<<1], nc[maxm<<1], tc;
int pre[maxn], fa[maxn], dep[maxn], pass;
bool brige[maxn], vis[maxn];
inline void init(){memset( head, -1, sizeof(head) );memset( dfn, 0, sizeof(dfn) );memset( brige, 0, sizeof(brige) );memset( c, 0, sizeof(c) );memset( vis, 0, sizeof(vis) );tot = 1;
}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!=-1; 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^1] = 1;          ans ++;}}else if( i!=(in_edge^1) ) 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!=-1; 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!=-1; i=nc[i] ){int y = vc[i];if( y!=fa[x] ){fa[y] = x;dep[y] = dep[x] + 1;dfs_lca(y);}}
}inline void LCA( int x, int y ){pass = 0;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 += 2;}
}int main(){// freopen("in.txt", "r", stdin);int kase = 1;while( ~scanf("%d%d", &n, &m), n||m ){init();for( int i=0; i<m; i++ ){int u, v;scanf("%d%d", &u, &v);add(u, v);}ans = dcc = num = 0;tarjan(1, 0);for( int i=1; i<=n; i++ ) if( !c[i] ) ++dcc, dfs_dcc(i);memset( hc, -1, sizeof(hc) );tc = 1;//不要使用map作为标记,遍历边进行新图的加边操作,map会TLEfor( int u=1; u<=n; u++ ){for( int i=head[u]; i!=-1; i=ed[i].next ){int v = ed[i].to;if( c[u]==c[v] ) continue;add_dcc(c[u], c[v]);}}ans = tc>>1;dep[1] = 1;fa[1] = 0;dfs_lca(1);           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 0;
}

先求桥,再求边双联通,再连边进行LCA

#include <iostream>
#include <cstring>
#include <cstdio>
#include <map>
#include <map>
using namespace std;const int maxn = 1e6 + 5;
const int maxm = maxn<<1;
struct edge{int to, next;
} ed[maxm<<1];
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<<1], nc[maxm<<1], tc;
int pre[maxn], fa[maxn], dep[maxn], pass;
bool ins[maxn], vis[maxn];
inline void init(){memset( head, -1, sizeof(head) );memset( dfn, 0, sizeof(dfn) );memset( c, 0, sizeof(c) );memset( vis, 0, sizeof(vis) );tot = 1;
}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] = 1;st[++st[0]] = x;for( int i=head[x]; i!=-1; i=ed[i].next ){int y = ed[i].to;if( i==(in_edge^1) ) 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[0]--];c[p] = dcc;ins[p] = 0;}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!=-1; i=nc[i] ){int y = vc[i];if( y!=fa[x] ){fa[y] = x;dep[y] = dep[x] + 1;dfs_lca(y);}}
}inline void LCA( int x, int y ){pass = 0;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 += 2;}
}int main(){// freopen("in.txt", "r", stdin);int kase = 1;while( ~scanf("%d%d", &n, &m), n||m ){init();for( int i=0; i<m; i++ ){int u, v;scanf("%d%d", &u, &v);add(u, v);}ans = dcc = num = 0;for( int i=1; i<=n; i++ ) if(!dfn[i]) tarjan(1, 0);memset( hc, -1, sizeof(hc) );tc = 1;for( int u=1; 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>>1;dep[1] = 1;fa[1] = 0;dfs_lca(1);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 0;
}

求强联通分量,再加边,进行LCA

转载于:https://www.cnblogs.com/WAautomaton/p/11176979.html

POJ 3694Network(Tarjan边双联通分量 + 缩点 + LCA并查集维护)相关推荐

  1. CSU2104: Extra Judicial Operation-Tarjan边双联通分量缩点两种方法-难受的bug

    (有任何问题欢迎留言或私聊 题目链接:CSU2104 题面:2017年南太平洋某区域赛题 The Suitably Protected Programming Contest (SPPC) is a ...

  2. 【割边缩点】解题报告:POJ - 3694 - Network(Tarjan割边缩点 + LCA + 并查集优化)

    POJ - 3694 - Network 给定一张N个点M条边的无向连通图,然后执行Q次操作,每次向图中添加一条边,并且询问当前无向图中"桥"的数量.N≤105,M≤2∗105,Q ...

  3. POJ 3694 (tarjan缩点+LCA+并查集)

    好久没写过这么长的代码了,题解东哥讲了那么多,并查集优化还是很厉害的,赶快做做前几天碰到的相似的题. 1 #include <iostream> 2 #include <algori ...

  4. POJ - 3694 Network(边双缩点+LCA+并查集优化)

    题目链接:点击查看 题目大意:给出一个由n个点组成的无向图,现在有m次操作,每次操作都会向图中增加一条无向边,每次操作后询问当前图中有多少个桥 题目分析:题意很好理解,思路也很好想,就是代码量有点小多 ...

  5. 【BZOJ2730】【codevs1996】矿场建设,点双联通分量

    传送门1 传送门2 思路: 前段时间学习的tarjan求双联通分量 练习的时候碰到了这样一道蛋疼的题 基本思路还是显而易见的,考虑分割联通快及计算大小来求出答案 由于之前写的都是边双联通分量,可以把无 ...

  6. 『Tarjan算法 无向图的双联通分量』

    无向图的双连通分量 定义:若一张无向连通图不存在割点,则称它为"点双连通图".若一张无向连通图不存在割边,则称它为"边双连通图". 无向图图的极大点双连通子图被 ...

  7. POJ-3352-RoadConstruction(边双联通分量,缩点)

    链接:https://vjudge.net/problem/POJ-3352#author=0 题意: 给一个无向连通图,至少添加几条边使得去掉图中任意一条边不改变图的连通性(即使得它变为边双连通图) ...

  8. HDU4612 Warm up —— 边双联通分量 + 重边 + 缩点 + 树上最长路

    题目链接:http://acm.split.hdu.edu.cn/showproblem.php?pid=4612 Warm up Time Limit: 10000/5000 MS (Java/Ot ...

  9. POJ 3177 Redundant Paths(边双联通分量)

    题目描述: In order to get from one of the F (1 <= F <= 5,000) grazing fields (which are numbered 1 ...

最新文章

  1. bootstrap modal使用方法
  2. 浅谈用原生 JS 模仿个Promise 的实现
  3. 32位微处理器的虚拟技术,是“坑爹”么!
  4. Android安装apk
  5. uibinder表单提交_使用UIBinder的GWT自定义按钮
  6. Go_ go mod 命令解决墙的问题
  7. php收购,php中文网收购全国用户量最大的phpstudy集成开发环境揭秘
  8. Linux内核同步机制之信号量与锁
  9. php 变量 类名,关于php:使用变量类名和静态方法时出错
  10. MyBatis3源码解析(4)参数解析
  11. IDEA使用技巧--在文件导航栏中屏蔽指定后缀名的文件
  12. hadoop学习视频
  13. MacOS11.6.7上安装Axure9.003720无法预览问题
  14. windows操作系统与linux操作系统相比各有什么优缺点
  15. 二进制、十进制和16进制对照表以及对应的字符
  16. win10计算机删除用户密码,怎么删除win10电脑上的账户密码
  17. 【算法】牛顿迭代法求平方根及多次方根
  18. 常见MFC UI界面库
  19. python抢票软件源代码_一百多行python代码实现抢票助手
  20. 辛普森 matlab,利用MATLAB软件编写辛普森求积公式程序

热门文章

  1. 这款App因涉嫌传销被罚7456万:会员层级达51级 收取佣金4.5亿
  2. 百度回应百科外链遭篡改:严厉打击 已报案
  3. 阿里巴巴北京总部鸟瞰图曝光:今天又是想去阿里上班的一天!
  4. 疑似小米9真机谍照首曝:后置三摄+屏幕指纹识别
  5. java设计模式之创建型设计模式
  6. flex布局导致拉伸的问题
  7. python123系统基本信息获取_Python运维-获取当前操作系统的各种信息
  8. vs2015 vs2017 编译zlib库
  9. ipfs c++client
  10. 搜索图片及相似度探秘 二