[题意]一个无向图可以有重边,下面q个操作,每次在两个点间连接一条有向边,每次连接后整个无向图还剩下多少桥(每次回答是在上一次连边的基础之上) [分析]好题,做完后涨了很多姿势~ 普通做法当然就是每加一条边重新算一次桥,但这样复杂度将达到O(q*M),显然要超时。。所以我们需要“动态地”在原图的基础上求桥~ 我们可以先把图求一次边双连通分量(BCC)然后缩点,因为同一双连通分量中没有桥,加边没有影响。一个很重要的性质就是一个图求一次边双连通分量缩点后将变成一颗树或者森林,并且树中的每条边都是桥}。因为此题中说所有的点都有边连着,所以这里缩点后是一棵树。 显然,树中每添加一条边,就会形成一个环,而这个环中的边将不再是桥,并且他们构成新的边双连通分量,所以我们每次在桥中减去这些边,并把他们缩成一个点。 第一,怎么求在树中加边(u,v)后形成的环? 我们可以求出u,v的LCA,然后环就是v->LCA(u,v)->u>(u,v)->v. 第二,怎么缩点? 以前一直做的是“静态缩点”,就是求一遍边BCC后图的结构就不变了,此时我们可以在求出每个点所属的BCC(bcc[i])后以BCC的标号来代替缩后的点。如果我们动态地加边,则每次都要修改原BCC中所有的点成新BCC,所以直接这样改行不通。这里我们是不是发现它很像……并查集?对!就是用并查集维护bcc[]!这是我做这道题学到的最重要的姿势:用并查集维护动态加边的缩点。 当然此题中我们没有用到bcc[],因为在求LCA时我们用的朴素的方法:{用level[]表示每个节点的深度,两个点同时向根爬,深度深的节点先爬(LCA(u,v) = LCA(u, father[v]) ),深度相同时两个点一起爬(LCA(u, v) = LCA(father[u], father[v]) ),直到两个点相同。}这种方法的好处是可以一边求LCA一边缩点。那么我们就需要一个father[]来维护节点的父节点。这样我们就不需要bcc[]了,直接用并查集维护father[],并用它表示缩点及所属BCC。


#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define MID(x,y) ((x+y)/2)
#define mem(a,b) memset(a,b,sizeof(a))
using namespace std;const int MAXV = 100005;
const int MAXE = 400005;
struct node{int u, v;int next;int opp;                    //把一个无向边拆成两个有向边,对应反向边标号
}arc[MAXE];
int cnt, head[MAXV];
void init(){cnt = 0;mem(head, -1);return ;
}
void add(int u, int v){arc[cnt].u = u;arc[cnt].v = v;arc[cnt].next = head[u];arc[cnt].opp = cnt + 1;head[u] = cnt ++;arc[cnt].u = v;arc[cnt].v = u;arc[cnt].next = head[v];arc[cnt].opp = cnt - 1;head[v] = cnt ++;return ;
}
int id, dfn[MAXV], low[MAXV];
int level[MAXV];
int father[MAXV];               //点的父节点,便于爬山坡(向根节点走),也用于并查集缩点
int bridge_num, bridge[MAXV];   //标记该边是不是桥,这里用桥的子节点表示
bool vis_arc[MAXE];             //一条边无向边(两个有向边)只访问一次,
int find(int u){                //并查集 + 路径压缩处理缩点, 缩点合并边时把子节点的父亲设为父节点if(father[u] != u)return father[u] = find(father[u]);elsereturn u;
}
void tarjan(int u, int l){dfn[u] = low[u] = ++id;level[u] = l;for (int i = head[u]; i != -1; i = arc[i].next){int v = arc[i].v;if (vis_arc[i]) continue;vis_arc[i] = vis_arc[arc[i].opp] = 1;if (!dfn[v]){father[v] = u;tarjan(v, l+1);low[u] = min(low[u], low[v]);}else{low[u] = min(low[u], dfn[v]);}if (dfn[u] < low[v]){bridge[v] = 1;bridge_num ++;}else{int x = find(u);int y = find(v);if (x != y)father[y] = x;}}
}
void solve(int n){id  = bridge_num = 0;mem(dfn, 0);mem(low, 0);mem(level, 0);mem(bridge, 0);mem(vis_arc, 0);for (int i = 0; i <= n; i ++)father[i] = i;for (int i = 1; i <= n; i ++){if (!dfn[i])tarjan(1, 1);}return ;
}
vector  path;
int lca(int u, int v){path.clear();if (level[u] > level[v])    swap(u, v);while(u != v){while(level[u] != level[v]){if(level[v] > level[u]){if (bridge[v]){bridge[v] = 0;bridge_num --;}path.push_back(v);v = father[v];}else{if (bridge[u]){bridge[u] = 0;bridge_num --;}path.push_back(u);u = father[u];}}while(u != v){if (bridge[v]){bridge[v] = 0;bridge_num --;}if (bridge[u]){bridge[u] = 0;bridge_num --;}path.push_back(u);path.push_back(v);v = father[v];u = father[u];}}int lc = u;//把加边后的环用并查集缩成一个点for (int i = 0; i < (int)path.size(); i ++){int u = path[i];father[u] = lc;}return bridge_num;
}
int main(){int n, m, t = 1;while(scanf("%d %d", &n, &m) != EOF){if (n + m == 0)break;init();for (int i = 0; i < m; i ++){int u, v;scanf("%d %d", &u, &v);add(u, v);}solve(n);int q;scanf("%d", &q);printf("Case %d:\n", t);for (int i = 0; i < q; i ++){int u, v;scanf("%d %d", &u, &v);printf("%d\n", lca(u, v));}puts("");t ++;}return 0;
}

转载于:https://www.cnblogs.com/AbandonZHANG/archive/2013/06/15/4114250.html

POJ 3694 Network ★(边双连通分量+并查集缩点+LCA)相关推荐

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

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

  2. BZOJ 2959: 长跑 [lct 双连通分量 并查集]

    2959: 长跑 题意:字词加入边,修改点权,询问两点间走一条路径的最大点权和.不一定是树 不是树? 把边双连通分量缩为一点! 怎么缩? 用一个并查集维护连通性,另一个并查集维护每个点所在边双的编号, ...

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

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

  4. POJ 1417 True Liars(路径压缩并查集+DP背包问题)

    POJ 1417 True Liars(路径压缩并查集+DP背包问题) http://poj.org/problem?id=1417 题意: 给出p1+p2个人,其中p1个是好人,p2个是坏人.然后有 ...

  5. POJ 1703 Find them, Catch them(并查集高级应用)

    POJ 1703 Find them, Catch them(并查集高级应用) 手动博客搬家:本文发表于20170805 21:25:49, 原地址https://blog.csdn.net/sunc ...

  6. CF-1023F.Mobile Phone Network(并查集缩点)

    CF-1023F.Mobile Phone Network(并查集缩点) 题目链接 题意 你手里有K条边还没有分配权值,已经存在M条边带权值,如何给你手中的边分配权值,使得K条边都在最后的最小生成树中 ...

  7. CodeForces - 1463E Plan of Lectures(拓扑排序+并查集缩点)

    题目链接:点击查看 题目大意:给出一棵有根树,树边都是有向边,再给出 kkk 个关系 (x,y)( x , y )(x,y),其意义是访问完点 xxx 后需要立即访问点 yyy,问是否存在一种合适的拓 ...

  8. 【POJ - 3694】Network(对dfn求lca 或 缩点+lca 或 边双连通+并查集)

    题干: 网络管理员管理大型网络.该网络由N台计算机和成对计算机之间的M链路组成.任何一对计算机都通过连续的链接直接或间接连接,因此可以在任何两台计算机之间转换数据.管理员发现某些链接对网络至关重要,因 ...

  9. POJ 3694 Network(tarjan+lca+并查集)

    题目 给定一张NNN个点MMM条边的无向连通图,然后执行QQQ次操作,每次向图中添加一条边,并且询问当前无向图中"桥"的数量. 题解 先求出图中所有的边双,然后缩点 令c[x],c ...

最新文章

  1. 5G时代,工业互联网安全挑战远超消费互联网
  2. V​M​W​a​r​e​里​安​装​6​4​位​L​i​n​u​x​ ​的​方​法
  3. 比特币的矿工为什么讨厌开发组Core?
  4. java公共excel导入_Java实现Excel的导入功能
  5. OTA整包的制作流程(未完)
  6. Win7共享文件夹简单?这个共享问题可以难倒90%的人
  7. 10-3的随笔继续讲,演绎“圆弧底”
  8. On the coexistence of transport protocols in data centers
  9. C 语言取整的几种方法6,C语言有以下几种取整方法:
  10. JAVA抽象类和接口的深入探讨
  11. 今日恐慌与贪婪指数为83 贪婪程度大幅上升
  12. 税控服务器管理系统已签名未上传,增值税发票管理系统升级后发票上传失败、勾选平台插件设置、勾选签名问题、勾选规则等热点问题...
  13. c语言自学 中文,C语言自学《一》 ---- 初探C语言
  14. ❤️【图文并茂】Chrome浏览器(油猴子)插件安装使用教程❤️
  15. 5G投资理财H5源码+带独家代理二开前端开发/学习培训专用型
  16. 浅谈京东静态html原理,京东首页前端架构设计.ppt
  17. 【Chrome】从Google官网下载 Google Chrome 离线安装包
  18. C语言 系统调用操作内核信号集
  19. 【企业网盘】公有云和私有云的9大差异
  20. Python升级到最新版本代码

热门文章

  1. 【Linux】一步一步学Linux——Bash常用快捷键(11)
  2. python统计行号_如何使用Python脚本分析CPU使用情况的?
  3. Codeforces 993C. Careful Maneuvering(详细注解)
  4. TeamViewer密码存储
  5. numpy使用MKL库提升计算性能
  6. HDU1569 方格取数(2)(二分图带权最大独立集 - 最小割应用)
  7. 成绩管理单链表文件c语言,c语言学生信息管理完整.docx
  8. java 获取mp4 缩略图_java获取视频缩略图
  9. GitLab CI/CD
  10. tensorflow 之 ValuError: At least two variables have the same name: bottom/bn1/beta_power0 等