POJ 3694 Network 双联通分量 + lca
2016-05-08 17:39
519 查看
题目:http://poj.org/problem?id=3694
题意:给定一个连通图,现有q个操作,每个操作连接两个点,求每次操作后图中有多少桥
思路:每次操作后用tarjan求桥肯定会T的,可以先求桥并标记,把边双连通分量缩点,缩成一棵树,然后对于每个查询,直接去找它们的lca,并把经过路径的桥去掉标记,因为路径上的桥都变成了圈的一部分,不再是桥了。不过做得时候不一定需要缩图。
总结:我的缩图写法有点搓,比不上大神的效率。。。
其一:不缩图
其二:缩图加并查集优化
题意:给定一个连通图,现有q个操作,每个操作连接两个点,求每次操作后图中有多少桥
思路:每次操作后用tarjan求桥肯定会T的,可以先求桥并标记,把边双连通分量缩点,缩成一棵树,然后对于每个查询,直接去找它们的lca,并把经过路径的桥去掉标记,因为路径上的桥都变成了圈的一部分,不再是桥了。不过做得时候不一定需要缩图。
总结:我的缩图写法有点搓,比不上大神的效率。。。
其一:不缩图
#include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<algorithm> #include<vector> #include<queue> using namespace std; const int N = 100100; struct edge { int to, next; } g[N*10]; int dfn , Dfn , low , head , father ; bool bridge ; int index, cnt, res; int n, m; void init() { memset(dfn, -1, sizeof dfn); memset(head, -1, sizeof head); memset(bridge, 0, sizeof bridge); index = cnt = res = 0; } void add_edge(int v, int u) { g[cnt].to = u; g[cnt].next = head[v]; head[v] = cnt++; } void tarjan(int v, int fa) { dfn[v] = low[v] = index++; bool f = true; int u; Dfn[v] = Dfn[fa] + 1; /*Dfn数组储存的是点的深度*/ for(int i = head[v]; i != -1; i = g[i].next) { u = g[i].to; if(u == fa && f) /*判重边*/ { f = false; continue; } if(dfn[u] == -1) { father[u] = v; tarjan(u, v); low[v] = min(low[v], low[u]); if(dfn[v] < low[u]) { res++; bridge[u] = true; } } else low[v] = min(low[v], dfn[u]); } } void lca(int v, int u) /*裸LCA*/ { if(Dfn[v] < Dfn[u]) swap(v, u); while(Dfn[v] > Dfn[u]) { if(bridge[v]) res--, bridge[v] = false; v = father[v]; } while(v != u) { if(bridge[v]) res--, bridge[v] = false; if(bridge[u]) res--, bridge[u] = false; v = father[v], u = father[u]; } } int main() { int q, a, b, x = 0; while(scanf("%d%d", &n, &m), n || m) { init(); for(int i = 0; i < m; i++) { scanf("%d%d", &a, &b); add_edge(a, b); add_edge(b, a); } tarjan(1, 0); scanf("%d", &q); printf("Case %d:\n", ++x); while(q--) { scanf("%d%d", &a, &b); lca(a, b); printf("%d\n", res); } } return 0; }
其二:缩图加并查集优化
#include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<algorithm> #include<vector> #include<queue> using namespace std; const int N = 100100; struct edge { int to, next; } g[N*10]; int dfn , low , st , dcc , head , deep , fat , par ; bool vis , bridge ; int index, cnt, top, num, sum; int a[N*2], b[N*2]; int n, m; void init() { for(int i = 1; i <= n; i++) par[i] = i; memset(dfn, -1, sizeof dfn); memset(head, -1, sizeof head); memset(bridge, 0, sizeof bridge); memset(vis, 0, sizeof vis); index = cnt = top = num = 0; } void add_edge(int v, int u) { g[cnt].to = u; g[cnt].next = head[v]; head[v] = cnt++; } void tarjan(int v, int fa) { dfn[v] = low[v] = index++; st[top++] = v; vis[v] = true; bool f = true; int u; for(int i = head[v]; i != -1; i = g[i].next) { u = g[i].to; if(u == fa && f) /*判重边*/ { f = false; continue; } if(dfn[u] == -1) { tarjan(u, v); low[v] = min(low[v], low[u]); } else if(vis[u]) low[v] = min(low[v], dfn[u]); } if(dfn[v] == low[v]) { num++; do { u = st[--top]; vis[u] = false; dcc[u] = num; } while(u != v); bridge[num] = true; /*缩点后的桥*/ } } void dfs(int v, int d) { vis[v] = true; deep[v] = d; int u; for(int i = head[v]; i != -1; i = g[i].next) if(!vis[u = g[i].to]) fat[u] = v, dfs(u, d + 1); } int ser(int x) { int r = x, i = x, j; while(r != par[r]) r = par[r]; while(i != r) j = par[i], par[i] = r, i = j; return r; } void lca(int v, int u) /*LCA*/ { v = ser(v), u = ser(u); if(v == u) return; if(deep[v] < deep[u]) swap(v, u); while(deep[v] > deep[u]) { if(bridge[v]) num--, bridge[v] = false; v = fat[v]; } while(v != u) { if(bridge[v]) num--, bridge[v] = false; if(bridge[u]) num--, bridge[u] = false; v = fat[v], u = fat[u]; } } int main() { int z = 0; while(scanf("%d%d", &n, &m), n || m) { init(); for(int i = 0; i < m; i++) { scanf("%d%d", a + i, b + i); add_edge(a[i], b[i]); add_edge(b[i], a[i]); } tarjan(1, -1); /*重新建图,缩点建树*/ memset(head, -1, sizeof head); cnt = 0; for(int i = 0; i < m; i++) if(dcc[a[i]] != dcc[b[i]]) { add_edge(dcc[a[i]], dcc[b[i]]); add_edge(dcc[b[i]], dcc[a[i]]); } memset(vis, 0, sizeof vis); dfs(dcc[1], 1); int q, x, y; scanf("%d", &q); num--; /*桥的个数为连通分量个数减1*/ printf("Case %d:\n", ++z); while(q--) { scanf("%d%d", &x, &y); lca(dcc[x], dcc[y]); printf("%d\n", num); } } return 0; }
相关文章推荐
- struts2中页面访问action的url问题,或许很简单
- excel截取单元格中部分内容
- ScrollView+ListView解决办法
- Android 干货App开发总结
- 关于EnumerateObjectsUsingBlock和for-in之间的较量
- php strtotime 和 date 日期操作
- C/C++自定义方式实现字符串的大小写转换
- php面向对象编程self和static的区别
- 把一个List集合有规律的插入另一个List集合中
- 20145326 《Java程序设计》实验五——Java网络编程及安全实验报告
- Bootstrap学习笔记
- 网站性能优化
- Filter
- java:排序算法
- 网络赚钱靠谱项目推荐和骗局陷阱
- (水)POJ-1862 哈夫曼树变形
- Xcode-Xcode 7.3 解决不能自动联想问题-备
- 设计模式之解释器模式
- 【Android安全】使用jarsigner命令对apk进行重签名
- windows下开源工程编译--x264源码编译过程