题目链接:点击查看

题目大意:给出一个由n个点组成的无向图,现在有m次操作,每次操作都会向图中增加一条无向边,每次操作后询问当前图中有多少个桥

题目分析:题意很好理解,思路也很好想,就是代码量有点小多(好几个模板拼拼凑凑出来的)

既然提到桥了,那么肯定先强连通缩一波点,将图重构成一棵树,求出原图中有多少个桥,随后每次操作都会加边,如果新边的两个端点属于同一个强连通块中,那么不会对答案做出贡献,直接输出答案就行了,如果不在同一个连通块中,假设两个点为x和y,那么加上这条边后,会对x->lca(x,y)->y这条边造成影响,具体影响就是这条边上的所有桥都成为一个连通分量了,故最后答案需要减去这条路径上的所有桥,并且将其标记一下,防止后续操作重复贡献

现在我们就是需要利用算法与数据结构实现上述操作了,很显然强连通缩点用tarjan,求lca用树上倍增就行,单纯因为好写。。最后在路径上更新答案可以暴力,因为这个题目的数据比较水,就是直接一个点一个点的走,时间复杂度为O(m+q*n),不过完全可以用并查集优化一下,其实用到并不是真的并查集,而是使用并查集路径压缩的思路,这样就能避免重复经过已经做出过贡献的桥了,时间复杂度优化为了O(m+q*logn)了,最后就是如何标记路径,这里我选择将边权映射给节点,因为我们在树上往上走的时候都是沿着父节点向上走,每个点的父节点都是唯一的,所以我们不妨将(父节点-当前节点)的这条路径映射到当前节点上来,具体实现看代码吧,就是多个模板拼凑起来的,代码量就比较多了

代码:

#include<iostream>
#include<cstdlib>
#include<string>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<climits>
#include<cmath>
#include<cctype>
#include<stack>
#include<queue>
#include<list>
#include<vector>
#include<set>
#include<map>
#include<sstream>
using namespace std;typedef long long LL;const int inf=0x3f3f3f3f;const int N=1e5+100;const int M=4e5+100;struct Egde
{int to,next;
}edge[M],edge2[M];int head[N],head2[N],low[N],dfn[N],c[N],f[N],num,cnt,cnt2,dcc,n,m,limit;bool bridge[M],vis[N];void addedge(int u,int v)
{edge[cnt].to=v;edge[cnt].next=head[u];head[u]=cnt++;
}void addedge2(int u,int v)
{edge2[cnt2].to=v;edge2[cnt2].next=head2[u];head2[u]=cnt2++;
}void tarjan(int u,int in_edge)
{dfn[u]=low[u]=++num;for(int i=head[u];i!=-1;i=edge[i].next){int v=edge[i].to;if(!dfn[v]){tarjan(v,i);low[u]=min(low[u],low[v]);if(low[v]>dfn[u])bridge[i]=bridge[i^1]=true;}else if(i!=(in_edge^1))low[u]=min(low[u],dfn[v]);}
}void dfs(int u)
{c[u]=dcc;for(int i=head[u];i!=-1;i=edge[i].next){int v=edge[i].to;if(c[v]||bridge[i])continue;dfs(v);}
}void init()
{limit=log2(n)+1;cnt2=cnt=num=dcc=0;memset(head2,-1,sizeof(head2));memset(head,-1,sizeof(head));memset(low,0,sizeof(low));memset(dfn,0,sizeof(dfn));memset(bridge,false,sizeof(bridge));memset(c,0,sizeof(c));memset(vis,false,sizeof(vis));
}void solve()
{for(int i=1;i<=n;i++)//找桥 if(!dfn[i])tarjan(i,0);for(int i=1;i<=n;i++)//缩点 if(!c[i]){dcc++;dfs(i);}
}void build()//缩点+连边
{solve();memset(f,-1,sizeof(f));for(int i=0;i<cnt;i+=2){int u=edge[i^1].to;int v=edge[i].to;if(c[u]==c[v])continue;addedge2(c[u],c[v]);addedge2(c[v],c[u]);}
}int deep[N],dp[N][20];void dfs2(int u,int fa,int dep)
{deep[u]=dep;dp[u][0]=fa;f[u]=fa;for(int i=1;i<=limit;i++)dp[u][i]=dp[dp[u][i-1]][i-1];for(int i=head2[u];i!=-1;i=edge2[i].next){int v=edge2[i].to;if(v==fa)continue;dfs2(v,u,dep+1);}
}int LCA(int x,int y)
{if(deep[x]<deep[y])swap(x,y);for(int i=limit;i>=0;i--)if(deep[x]-deep[y]>=(1<<i))x=dp[x][i];if(x==y)return x;for(int i=limit;i>=0;i--)if(dp[x][i]!=dp[y][i]){x=dp[x][i];y=dp[y][i];}return dp[x][0];
}int cal(int x,int y)
{int lca=LCA(x,y);int ans=0;while(deep[x]>deep[lca]){if(!vis[x]){ans++;vis[x]=true;}int xx=f[x];f[x]=lca;x=xx;}while(deep[y]>deep[lca]){if(!vis[y]){ans++;vis[y]=true;}int yy=f[y];f[y]=lca;y=yy;}return ans;
}int main()
{
//  freopen("input.txt","r",stdin);
//  ios::sync_with_stdio(false);int kase=0;while(scanf("%d%d",&n,&m)!=EOF&&n+m){init();while(m--){int u,v;scanf("%d%d",&u,&v);addedge(u,v);addedge(v,u);}build();dfs2(1,0,0);int ans=dcc-1;scanf("%d",&m);printf("Case %d:\n",++kase);while(m--){int u,v;scanf("%d%d",&u,&v);if(c[u]!=c[v])//不一个连通块内ans-=cal(c[u],c[v]);printf("%d\n",ans);}}return 0;
}

POJ - 3694 Network(边双缩点+LCA+并查集优化)相关推荐

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

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

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

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

  3. POJ 3694 Network ★(边双连通分量+并查集缩点+LCA)

    [题意]一个无向图可以有重边,下面q个操作,每次在两个点间连接一条有向边,每次连接后整个无向图还剩下多少桥(每次回答是在上一次连边的基础之上) [分析]好题,做完后涨了很多姿势~ 普通做法当然就是每加 ...

  4. POJ 3694Network(Tarjan边双联通分量 + 缩点 + LCA并查集维护)

    [题意]: 有N个结点M条边的图,有Q次操作,每次操作在点x, y之间加一条边,加完E(x, y)后还有几个桥(割边),每次操作会累积,影响下一次操作. [思路]: 先用Tarjan求出一开始总的桥的 ...

  5. POJ 1703 Find them, Catch them(路径压缩并查集)

    POJ 1703 Find them, Catch them(路径压缩并查集) 2014年03月11日 20:13:54 阅读数:881 POJ 1703 Find them, Catch them( ...

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

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

  7. ZOJ 4097 Rescue the Princess 边双缩点+LCA

    给你一个图和三个点U,V,W  问你是否存在从U到V和从U到W的两条边不相交路径 先边双缩点 再每个连通分量搞LCA 最后LCA判 #include<bits/stdc++.h> usin ...

  8. 51nod1743-雪之国度【最小生成树,LCA,并查集】

    正题 题目链接:http://www.51nod.com/Challenge/Problem.html#problemId=1743 题目大意 nnn个点mmm条边的一张图,每次询问要求找出x,yx, ...

  9. POJ 1984 Navigation Nightmare 【经典带权并查集】

    任意门:http://poj.org/problem?id=1984 Navigation Nightmare Time Limit: 2000MS   Memory Limit: 30000K To ...

最新文章

  1. linux nvme分区,这些 loop 分区是什么鬼东西?
  2. Android源码分析(十一)-----Android源码中如何引用aar文件
  3. Nuxt.Js爬坑小记
  4. css3中3d旋转中rotatex,rotatey,rotatez的旋转正方向
  5. java命令执行的三种方式
  6. LeetCode(查找元素的第一个和最后一个位置)
  7. 车机芯片:今后买车就像从前配电脑
  8. linux环境变量lang=c,设置linux环境变量LANG
  9. 品牌连锁店5G/4G无线组网方案
  10. java对pdf分割_Java如何使用Java将PDF分割成多个文档?
  11. 迁移学习和微调深度卷积神经网络
  12. 测试工程师多年面试问题整理
  13. JavaScript的事件系列二keydown,keypress,获取键码------JavaScript学习之路4
  14. 正则表达式在线测试一
  15. 【C++】虚函数与虚函数表
  16. IBMX3650M4安装win2008Server操作指南
  17. 王阳明:人心本是光明之镜 奈何因私欲黯淡无光
  18. 浅析Android Root
  19. Revit中的默认约束问题
  20. Android 图片预览器加载微博长图,大图

热门文章

  1. java按行写入txt文件内容_JAVA编程:读文件,按行输出文件内容
  2. Sentinel授权规则及规则持久化
  3. 项目前端页面框架介绍
  4. 下单问题分析及解决方式
  5. mybatis 批量将list数据插入到数据库
  6. oracle11g同步,Oracle11g三种数据同步方式-Oracle
  7. Spring Cloud依赖
  8. JVM--类加载机制
  9. linux ../的含义
  10. sq语句报错Parameter index out of range (2 > number of parameters, which is 1).