poj 3694Network 双联通分量+lca
2016-05-18 17:29
260 查看
输入:
3 2
1 2
2 3
2
1 2
1 3
题意就是先输入n,m 表示一颗树有n个节点,m条无向边。然后又q次操作, 每一次操作 输入i,j 表示在节点i和节点j之间加一条边,并且询问在加边之后的图中存在多少个桥。
在这里先感谢大牛:
/article/6666420.html
他的博客真的给了我很多的帮助,在我犯了很多很多错误的时候 用很简短很清晰的代码拨正了我的错误思维。膜一发
这题 我一开始是这样写的: 先dfs 跑一发tarjan,把dfn,low 桥 连通块 都跑出来。 然后再 dfs 跑一发st的lca在线询问, 然后对于加边 i,j 我们就从i 向上遍历到 lca,从j 向上遍历到lca,扫描减少的桥的数量。 不知道是不是姿势写的太丑,还是两遍dfs耗时间,反正就是T了。 (现在想想 好像是因为 fa[i] 写的有问题才会T)
然后膜了一下别人的代码, 感觉至少比我的代码少 100 行 TUT,我好菜啊
感觉自己没有学懂tarjan啊,好菜啊,明明一遍dfs就可以做完,因为我们后面lca 用很朴素的想法的话用dfn和low就可以完成了,话不多说 上代码:
3 2
1 2
2 3
2
1 2
1 3
题意就是先输入n,m 表示一颗树有n个节点,m条无向边。然后又q次操作, 每一次操作 输入i,j 表示在节点i和节点j之间加一条边,并且询问在加边之后的图中存在多少个桥。
在这里先感谢大牛:
/article/6666420.html
他的博客真的给了我很多的帮助,在我犯了很多很多错误的时候 用很简短很清晰的代码拨正了我的错误思维。膜一发
这题 我一开始是这样写的: 先dfs 跑一发tarjan,把dfn,low 桥 连通块 都跑出来。 然后再 dfs 跑一发st的lca在线询问, 然后对于加边 i,j 我们就从i 向上遍历到 lca,从j 向上遍历到lca,扫描减少的桥的数量。 不知道是不是姿势写的太丑,还是两遍dfs耗时间,反正就是T了。 (现在想想 好像是因为 fa[i] 写的有问题才会T)
然后膜了一下别人的代码, 感觉至少比我的代码少 100 行 TUT,我好菜啊
感觉自己没有学懂tarjan啊,好菜啊,明明一遍dfs就可以做完,因为我们后面lca 用很朴素的想法的话用dfn和low就可以完成了,话不多说 上代码:
#include <cstdio> #include <cmath> #include <cstring> #include <ctime> #include <iostream> #include <algorithm> #include <set> #include <vector> #include <sstream> #include <queue> #include <typeinfo> #include <fstream> #include <map> #include <stack> typedef long long ll; using namespace std; const int N = 100010;//点数 const int MAXM = 200010;//边数,因为是无向图,所以这个值要*2 int head ,dfn ,low ,vis ,fa ,dcnt,bridge; int isbri ; struct Edge{ int u,v,next; int used; //判断是否为桥 int bri; }edge[2*MAXM]; int tot; void addedge(int u,int v){ edge[tot].v=v; edge[tot].used=0; edge[tot].next=head[u]; head[u]=tot++; } void dfs(int u,int pre){ // printf("u=%d\n",u); vis[u]=1; dfn[u]=low[u]=++dcnt; for(int i=head[u];i!=-1;i=edge[i].next){ int v=edge[i].v; // if(v==pre) continue; if(!edge[i].used){ edge[i].used=edge[i^1].used=1; if(!vis[v]){ fa[v]=u; dfs(v,u); if( low[u] > low[v] ) low[u] = low[v]; if(low[v] > dfn[u]){ //桥 bridge++; isbri[v]=1; // 精妙: 只会设置每一条路的终点,反向并不会设置 } } else if(vis[u] && low[u]> dfn[v]) low[u]=dfn[v]; } } } void LCA(int u,int v){ //重点来了, 其实LCA完全没必要再扫一遍,借助low 和 dfn 完全够实现lca if(dfn[u] < dfn[v]) swap(u,v); // dfn 即 深度 while(dfn[u] > dfn[v]){ //因为 u是较深的那个点,先尽量往上移动 if(isbri[u]) bridge--; isbri[u]=0; u=fa[u]; } while(dfn[v]>dfn[u]){ if(isbri[v]) bridge--; isbri[v]=0; v=fa[v]; } while(u!=v){ if(isbri[u]) bridge--; if(isbri[v]) bridge--; isbri[u]=isbri[v]=0; u=fa[u]; v=fa[v]; } //最后 u=v , 这个u就是lca,但我不说 //讲道理 复杂度有点高啊 } bool flag ; void init(){ tot=0; memset(vis,0,sizeof(vis)); memset(head,-1,sizeof(head)); memset(isbri,0,sizeof(isbri)); memset(flag,0,sizeof(flag)); dcnt = bridge = 0; } int main(){ // freopen("1.txt","r",stdin); int n,m,cas=0; while(~scanf("%d %d",&n,&m) && (n||m)){ init(); printf("Case %d:\n",++cas); for(int i=1;i<=m;i++){ int u,v; scanf("%d %d",&u,&v); addedge(u,v); addedge(v,u); flag[v]=true; } dfs(1,1); int q; // printf("bri=%d\n",bridge); // for(int i=1;i<=n;i++){ // printf("%d ",dfn[i]); // } // cout<<endl; scanf("%d",&q); while(q--){ int a,b; scanf("%d %d",&a,&b); if(bridge==0){ printf("0\n"); continue; } LCA(a,b); printf("%d\n",bridge); } } return 0; }
相关文章推荐
- zk客户端命令
- java ThreadLocal 自己的一些见解
- getCompoundDrawables
- ipython for win7 安装
- 修改Hive表结构
- iotop类python代码
- Django相关
- 算法设计☞棋盘覆盖
- RHEL7学习笔记_Linux文件系统
- 在Linux终端中使用tree命令
- Java 内部类种类及使用解析
- jquery的ajax方法之一:load
- listview setselection 无效解决办法
- 【Arduino官方教程第一辑】示例程序 4-8 串口双向调用 (握手连接)
- 关于mysql的java.sql.SQLException: Incorrect string value: '\xF0\x9F\x91\xBD\xF0\x9F…'问题
- 线段树题(2.0版)
- linux下处理图片时需加上参数“-Djava.awt.headless=true”
- java web工程启动socket服务
- 用两个栈实现队列
- Windows IO方法