热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

POJ3694NetworkTarjan求边双连通+LCS+并查集

POJ3694NetworkTarjan求边双连通+LCS+并查集题目链接:POJ3694Network题意:给定N个顶点M条边的无向图。N(1≤N≤100,0
POJ 3694 Network Tarjan求边双连通+LCS+并查集
题目链接: POJ 3694 Network 
题意:给定 N个顶点M条边的无向图。N(1 ≤ N ≤ 100,000) and M(N - 1 ≤ M ≤ 200,000). 数据保证 图是连通的。 然后Q个操作(Q <1000), 每次往图中添加一条边,每次操作之后给出图中还有多少个桥(割边)。
思路:这是一道很有意思的题目!首先对原图进行边双连通缩点,求出DAG,因为初始的无向图是连通的,那么求出来的DAG是一棵树(PS:为了以后求LCA,我需要保存每个节点的父节点和深度),树上的边全是桥,这是无向连通图的性质嘛。
求出DAG树之后,我们理解为我们新构建了一个 有向图
然后每次添加一条边(u, v),如果两个顶点在原图中的同一个边双连通,毫无疑问,不需要处理。反之, 就向上找最近公共祖先节点设为x,并把沿途的节点加入并查集中,因为添加一条边,相当于在树上增加一条边,即形成了 u → x → 的一个环,那么 环上所有的边都不是桥了,这是显而易见的。
求公共祖先节点的时候利用了 并查集 压缩路径 的一个思想,DAG图中,同一个强连通分量中,深度高的节点直接跳转到他的祖先的位置(我让祖先一定是同一个强连通分量中深度最低的节点,并查集中 合并的时候可以保证到这点),这样就不必要一个个向上走了,这个在 代码中有所体现。
这样就是344MS水过了。 微笑
测试数据 可以参考 Discuss 提供的测试数据。 注意, 无向图边数要开双倍,时刻记住,谨记谨记~
#include 
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;

#define FIN freopen("input.txt","r",stdin)
#define FOUT freopen("output.txt","w",stdout)
#define fst first
#define snd second
typedef __int64 LL;
typedef pair PII;

const int MAXN = 100000 + 5;
const int MAXM = 200000 + 5;

struct Edge {
int v, next;
Edge() {}
Edge(int v, int next) : v(v), next(next) {}
} edge[MAXM <<1];
struct TNode {
int fa, depth;
TNode() {}
TNode(int fa, int depth) : fa(fa), depth(depth) {}
} tree[MAXN];
int head[MAXN], tot;
int low[MAXN], dfn[MAXN], belong[MAXN], stack[MAXN], top, Index, bridge, block;
bool inStack[MAXN];
bool vis[MAXN];
int par[MAXN];
int N, M, Q;

void init() {
block = bridge = tot = Index = top = 0;
memset(head, -1, sizeof(head));
memset(dfn, 0, sizeof(dfn));
memset(inStack, false, sizeof(inStack));
memset(vis, false, sizeof(vis));
}
void add_edge(int& u, int& v) {
edge[tot] = Edge(v, head[u]);
head[u] = tot++;
}
void Tarjan(int u, int pre) {
int v;
low[u] = dfn[u] = ++Index;
stack[top++] = u;
inStack[u] = true;
bool flag = false; // 跳过反向边
for (int i = head[u]; ~i; i = edge[i].next) {
v = edge[i].v;
if (v == pre && !flag) {
flag = true;
continue;
}
if (!dfn[v]) {
Tarjan(v, u);
if (low[u] > low[v]) low[u] = low[v];
if (low[v] > dfn[u]) {
bridge ++;
}
} else if (inStack[v] && low[u] > dfn[v]) low[u] = dfn[v];
}
if (low[u] == dfn[u]) {
block++;
do {
v = stack[--top];
inStack[v] = false;
belong[v] = block;
} while (u != v);
}
}
void build_tree(const int u, const int pre, const int depth) {
int v;
vis[u] = true;
tree[belong[u]] = TNode(belong[pre], depth);
for (int i = head[u]; ~i; i = edge[i].next) {
v = edge[i].v;
if (vis[v]) continue;
if (v == pre) continue;
build_tree(v, u, belong[u] == belong[v] ? depth : depth + 1);
}
}
int Find(const int x) {
return x == par[x] ? x : (par[x] = Find(par[x]));
}
void Union(const int& a, const int& b) {
int pa = Find(a), pb = Find(b);
if (pa == pb) return;
par[pa] = pb;
Find(a), Find(b);
}
void lca(int a, int b) {
if (Find(a) == Find(b)) return;
while (tree[a].depth if (Find(tree[b].fa) != Find(b)) bridge --;
Union(b, tree[b].fa);
b = Find(b);
}
while (tree[a].depth > tree[b].depth) {
if (Find(tree[a].fa) != Find(a)) bridge --;
Union(a, tree[a].fa);
a = Find(a);
}
while (a != b) {
if (Find(tree[a].fa) != Find(a)) bridge --;
if (Find(tree[b].fa) != Find(b)) bridge --;
Union(a, tree[a].fa);
Union(b, tree[b].fa);
a = Find(a);
b = Find(b);
}
}

int main() {
#ifndef ONLINE_JUDGE
FIN;
#endif // ONLINE_JUDGE
int u, v, cas = 0;
while (~scanf("%d %d", &N, &M) && N) {
init();
for (int i = 0; i scanf("%d %d", &u, &v);
add_edge(u, v);
add_edge(v, u);
}
Tarjan(1, 0);
build_tree(1, 1, 1);
for (int i = 1; i <= block; i++) par[i] = i;
scanf("%d", &Q);
printf("Case %d:\n", ++cas);
while (Q--) {
scanf("%d %d", &u, &v);
lca(belong[u], belong[v]);
printf("%d\n", bridge);
}
puts("");
}
return 0;
}


推荐阅读
author-avatar
古韵卡次
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有