poj3694+hdu2460 求桥+缩点+LCA/tarjan
这个题使我更深理解了TARJAN算法,题意:无向图,每添加一条边后文桥的数量,三种解法:(按时间顺序),1,暴力,每每求桥,听说这样能过,我没过,用的hash判重,这次有俩个参数(n->10w,开不了二维的),怎么判?联系2个参数,我想到了用一个函数,像散列一样,定义关系,我随便写了一个hash[x+y+x/y+y/x+x%y+y%x+x|y],一直WA,虽然未过,但是想到了这个,以后2个参数判重可以用之!2.网上学习了算法,将之缩点成树,每个双连通分量用一个点表示,用一个数组tree[i],点i属于tree[i]值,然后记录下桥,重新用一个FATHER【i】来建树,具体见代码。3.学习了算法后,其实不用缩点!直接搞起!因为tarjan算法本生成树!用后面一个点来标记边即可啊!(开始总部知道怎么标记!)并更加理解了树枝边和返祖边,桥是树枝边,无向图的所有边分为树枝边和返祖边(后向边),都是实际存在的。直接在原图上找LCA(按DFN值来判断)即可。
//(原创于2014.2.18)今复习之用,重新理解后,重新编辑方法3。
在hduj交爆栈,前加一句话:即可AC
- #pragma comment(linker, "/STACK:10240000000000,10240000000000")// 申请空间
- #include<iostream> //暴力,WA
- #include<vector>
- #include<cstring>
- #include<cstdio>
- using namespace std;
- vector<vector<int> >edge(100001);
- int dfn[100001];
- int low[100001];
- int visited[100001]; //标记访问
- int times=0; //时间戳
- int hash[210001];
- int num_bridge=0;
- int min(int a,int b)
- {
- if(a<=b)return a;
- return b;
- }
- void tarjan(int u,int fa) //dfs
- {
- dfn[u]=low[u]=++times;
- int daxiao=edge[u].size();
- for(int i=0;i<daxiao;i++)
- {
- int child=edge[u][i];
- if(visited[child]==0)
- {
- visited[child]=1;
- tarjan(child,u);
- low[u]=min(low[u],low[child]);
- if(dfn[u]<low[child]&&hash[u+child+2*u%child+2*child%u+3*u/child+3*child/u]<=1) //是桥
- {
- num_bridge++;
- }
- }
- else if(edge[u][i]!=fa)
- {
- low[u]=min(dfn[edge[u][i]],low[u]);
- }
- }
- }
- int main()
- {
- int n,m;
- int tcase=1;
- while (~scanf("%d%d",&n,&m)&&(n||m))
- {
- for(int i=0;i<=n;i++)
- {
- edge[i].clear();
- }
- for(int i=0;i<210001;i++)
- {
- hash[i]=0;
- }
- int a,b;
- for(int i=0;i<m;i++)
- {
- scanf("%d%d",&a,&b);
- edge[a].push_back(b);
- edge[b].push_back(a);
- hash[a+b+2*a%b+2*b%a+3*a/b+3*b/a]++;
- }
- int que;scanf("%d",&que);
- printf("Case %d:\n",tcase);
- tcase++;
- while(que--)
- {
- times=0;
- num_bridge=0;
- for(int i=0;i<=n;i++)
- {
- low[i]=dfn[i]=visited[i]=0;
- }
- scanf("%d%d",&a,&b);
- edge[a].push_back(b);
- edge[b].push_back(a);
- hash[a+b+2*a%b+2*b%a+3*a/b+3*b/a]++;
- visited[1]=1;
- tarjan(1,-1);
- printf("%d\n",num_bridge);
- }
- printf("\n");
- }
- return 0;
- }
- #include<iostream> //方法2,poj 2000MS AC,HOJ RE(stack over)
- #include<vector>
- #include<cstring>
- #include<cstdio>
- #include<stack>
- #include<queue>
- using namespace std;
- int dfn[100001]; //
- int low[100001];
- int visited[100001]; //tarjan标记访问
- int father[100001]; //缩点后建成一棵树
- int head[100001];
- bool instack[100001];
- int tree[100001]; //每个边双连通分量中的点属于一个集合,函数值1-block
- int level[100001];
- bool mark[100001]; //dfs标记
- bool is_bridge[100001]; //标记桥,形成树后,用点来标记边也可,
- stack<int>s;
- vector<vector<int> >bridge(100001);
- int min(int a,int b)
- {
- if(a<=b)return a;
- return b;
- }
- struct edges //边
- {
- int pre,to;
- };
- struct bridges //桥
- {
- int from,to;
- };
- int times=0; int num_bridge=0; int block; //时间戳,桥数量,“块”数(边双连通分量数)
- vector<edges>edge(400001); vector<bridges> ve;
- void tarjan(int u,int fa) //走一遍,求出桥
- {
- dfn[u]=low[u]=++times;
- instack[u]=1;
- s.push(u); //入栈,
- for(int i=head[u];i!=-1;i=edge[i].pre)
- {
- int child=edge[i].to;
- if(visited[child]==0)
- {
- visited[child]=1;
- tarjan(child,u);
- low[u]=min(low[u],low[child]);
- if(dfn[u]<low[child]) //是桥,保存起来
- {
- num_bridge++;
- bridges temp;
- temp.from=u;temp.to=child;
- ve.push_back(temp);
- }
- }
- else if(child!=fa)
- {
- low[u]=min(dfn[child],low[u]);
- }
- }
- if(dfn[u]==low[u]) //发现一个边双连通分量 blosk++
- {
- block++;
- int now=s.top();
- while(now!=u)
- {
- instack[now]=0;
- s.pop();
- tree[now]=block;
- now=s.top();
- }
- instack[now]=0;
- s.pop();
- tree[now]=block;
- }
- }
- void dfs(int u,int lev) //走一遍DFS,自制缩成一棵树,用father【i】来连接,规定了方向,并记录每个点深度。
- {
- level[u]=lev;
- int len=bridge[u].size();
- for(int i=0;i<len;i++)
- {
- int v=bridge[u][i];
- if(mark[v]==0)
- {
- father[v]=u;
- mark[v]=1;
- dfs(v,lev+1);
- }
- }
- }
- void lca(int u,int v) //每次询问添加的边,调用一次LCA,将路经上(按father和深度level向上走)的标记为非桥。
- {
- if(level[u]>level[v]){int temp=v;v=u;u=temp;}
- while(level[v]>level[u])
- {
- if(is_bridge[v])
- {
- num_bridge--;
- is_bridge[v]=0;
- }
- v=father[v];
- }
- while(v!=u)
- {
- if(is_bridge[v])
- {
- num_bridge--;
- is_bridge[v]=0;
- }
- if(is_bridge[u])
- {
- num_bridge--;
- is_bridge[u]=0;
- }
- v=father[v];
- u=father[u];
- }
- }
- int main()
- {
- int n,m;
- int tcase=1;
- while (~scanf("%d%d",&n,&m)&&(n||m))
- {
- for(int i=0;i<100001;i++)
- {
- level[i]=mark[i]=tree[i]=father[i]=low[i]=dfn[i]=visited[i]=0;
- head[i]=-1;
- bridge[i].clear();
- is_bridge[i]=1;
- }
- ve.clear();
- int a,b;
- num_bridge=block=times=0;
- for(int i=0;i<2*m;i++) //读入
- {
- scanf("%d%d",&a,&b);
- edge[i].to=b;
- edge[i].pre=head[a];
- head[a]=i;
- i++;
- edge[i].to=a;
- edge[i].pre=head[b];
- head[b]=i;
- }
- visited[1]=1;
- tarjan(1,-1);
- for(int i=0;i<ve.size();i++)
- {
- bridge[tree[ve[i].from]].push_back(tree[ve[i].to]);
- bridge[tree[ve[i].to]].push_back(tree[ve[i].from]);
- }
- mark[1]=1;
- dfs(1,0);
- int que;scanf("%d",&que);
- printf("Case %d:\n",tcase);
- tcase++;
- while(que--)
- {
- scanf("%d%d",&a,&b);
- lca(tree[a],tree[b]);
- printf ("%d\n",num_bridge);
- }
- printf("\n");
- }
- return 0;
- }
方法3:
- #pragma comment(linker, "/STACK:10240000000000,10240000000000")// 申请空间,否则爆。Hdu 460MS ac
- #include<iostream> //poj 1000MS AC,
- #include<vector>
- #include<cstring>
- #include<cstdio>
- using namespace std;
- int dfn[100001];
- int low[100001];
- bool visited[100001]; //tarjan标记访问
- int father[100001]; //缩点后建成一棵树
- int head[100001]; //每个边双连通分量中的点属于一个集合,函数值1-block
- bool is_bridge[100001]; //标记桥,形成树后,用点来标记边也可,
- int min(int a,int b)
- {
- if(a<=b)return a;
- return b;
- }
- struct edges //前向星保存边
- {
- int pre,to;
- };
- int times=0; int num_bridge=0; //时间戳,桥数量,
- vector<edges>edge(400001);
- void tarjan(int u,int fa) //走一遍,求出桥,不忘了。dfs生成的是树!所以可以标记后面一个点来标记边!无向图tarjan俩个变量
- {
- dfn[u]=low[u]=++times;
- for(int i=head[u];i!=-1;i=edge[i].pre)
- {
- int child=edge[i].to;
- if(visited[child]==0) //理解这里是树枝边(向前)!
- {
- visited[child]=1;
- father[child]=u; //生成树的父亲
- tarjan(child,u);
- low[u]=min(low[u],low[child]);
- if(dfn[u]<low[child]) //是桥,保存起来,标记后一个点即可(生成树每个点对应一条到它的边)
- {
- num_bridge++;
- is_bridge[child]=1;
- }
- }
- else if(child!=fa) //返祖边(向后边)点已经访问,说明child是u的某个祖先!
- {
- low[u]=min(dfn[child],low[u]);
- }
- }
- }
- void lca(int u,int v) //每次询问添加的边,调用一次LCA,将路经上(按father和dfn向上走)的标记为非桥。dfn恰好是树的的层次。
- {
- if(dfn[u]>dfn[v]){int temp=v;v=u;u=temp;}
- while(dfn[v]>dfn[u]) //到同一层为止。
- {
- if(is_bridge[v])
- {
- num_bridge--;
- is_bridge[v]=0;
- }
- v=father[v];
- }
- while(v!=u) //到公共祖先为止。
- {
- if(is_bridge[v])
- {
- num_bridge--;
- is_bridge[v]=0;
- }
- if(is_bridge[u])
- {
- num_bridge--;
- is_bridge[u]=0;
- }
- v=father[v];
- u=father[u];
- }
- }
- int main()
- {
- int n,m;
- int tcase=1;
- while (~scanf("%d%d",&n,&m)&&(n||m))
- {
- for(int i=0;i<100001;i++)
- {
- dfn[i]=father[i]=low[i]=dfn[i]=visited[i]=0;
- head[i]=-1;
- is_bridge[i]=0;
- }
- int a,b;
- num_bridge=times=0;
- for(int i=0;i<2*m;i++) //读入
- {
- scanf("%d%d",&a,&b);
- edge[i].to=b;
- edge[i].pre=head[a];
- head[a]=i;
- i++;
- edge[i].to=a;
- edge[i].pre=head[b];
- head[b]=i;
- }
- visited[1]=1;
- tarjan(1,-1);
- int que;scanf("%d",&que);
- printf("Case %d:\n",tcase);
- tcase++;
- while(que--)
- {
- scanf("%d%d",&a,&b);
- lca(a,b);
- printf ("%d\n",num_bridge);
- }
- printf("\n");
- }
- return 0;
- }
poj3694+hdu2460 求桥+缩点+LCA/tarjan的更多相关文章
- HDU-4612 Warm up,tarjan求桥缩点再求树的直径!注意重边
Warm up 虽然网上题解这么多,感觉写下来并不是跟别人竞争访问量的,而是证明自己从前努力过,以后回头复习参考! 题意:n个点由m条无向边连接,求加一条边后桥的最少数量. 思路:如标题,tarjan ...
- 无向连通图求割边+缩点+LCA
Network Time Limit: 5000MS Memory Limit: 65536K Total Submissions: 7082 Accepted: 2555 Descripti ...
- hdoj 4612 Warm up【双连通分量求桥&&缩点建新图求树的直径】
Warm up Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 65535/65535 K (Java/Others)Total Su ...
- POJ3694 Network 边双缩点+LCA+并查集
辣鸡错误:把dfs和ldfs搞混...QAQ 题意:给定一个无向图,然后查询q次,求每次查询就在图上增加一条边,求剩余割边的个数. 先把边双缩点,然后预处理出LCA的倍增数组: 然后加边时,从u往上跳 ...
- POJ 3694 Network(无向图求桥+重边处理+LCA)
题目大意: 给你一个无向图,然后再给你一个Q代表有Q次询问,每一次加一条边之后还有几座桥.在这里要对重边进行处理. 每次加入一条边之后,在这条搜索树上两个点的公共祖先都上所有点的桥都没了. 这里重边的 ...
- Tarjan算法应用 (割点/桥/缩点/强连通分量/双连通分量/LCA(最近公共祖先)问题)(转载)
Tarjan算法应用 (割点/桥/缩点/强连通分量/双连通分量/LCA(最近公共祖先)问题)(转载) 转载自:http://hi.baidu.com/lydrainbowcat/blog/item/2 ...
- tarjan算法(强连通分量 + 强连通分量缩点 + 桥(割边) + 割点 + LCA)
这篇文章是从网络上总结各方经验 以及 自己找的一些例题的算法模板,主要是用于自己的日后的模板总结以后防失忆常看看的, 写的也是自己能看懂即可. tarjan算法的功能很强大, 可以用来求解强连通分量, ...
- 【并查集缩点+tarjan无向图求桥】Where are you @牛客练习赛32 D
目录 [并查集缩点+tarjan无向图求桥]Where are you @牛客练习赛32 D PROBLEM SOLUTION CODE [并查集缩点+tarjan无向图求桥]Where are yo ...
- POJ 3694 (tarjan缩点+LCA+并查集)
好久没写过这么长的代码了,题解东哥讲了那么多,并查集优化还是很厉害的,赶快做做前几天碰到的相似的题. #include <iostream> #include <algorithm& ...
随机推荐
- 实现流水灯以间隔500ms的时间闪烁(系统定时器SysTick实现的精确延时)
/** ****************************************************************************** * @file main.c * ...
- AWS Data Lake Service Stack
- (转)编码剖析Spring管理Bean的原理
http://blog.csdn.net/yerenyuan_pku/article/details/52832434 在Spring的第一个案例中,我们已经知道了怎么将bean交给Spring容器进 ...
- (转)Spring的三种实例化Bean的方式
http://blog.csdn.net/yerenyuan_pku/article/details/52832793 Spring提供了三种实例化Bean的方式. 使用类构造器实例化. <be ...
- 【整理】iview中刷新页面的时候更新导航菜单的active-name
iview中刷新页面的时候更新导航菜单的active-name https://blog.csdn.net/lhjuejiang/article/details/83212070
- faster rcnn训练过程讲解
http://blog.csdn.net/u014696921/article/details/60321425
- android滚动图片
关于广告轮播,大家肯定不会陌生,它在现手机市场各大APP出现的频率极高,它的优点在于"不占屏",可以仅用小小的固定空位来展示几个甚至几十个广告条,而且动态效果很好,具有很好的用户& ...
- C# string补位
参考:https://www.cnblogs.com/zhangqs008/archive/2012/02/01/2341078.html //1.1.左补位 "; , 'A'); //1. ...
- 牛客noip前集训营(第一场)提高T1
链接:https://www.nowcoder.com/acm/contest/172/A来源:牛客网 题目描述 小N得到了一个非常神奇的序列A.这个序列长度为N,下标从1开始.A的一个子区间对应一个 ...
- perl学习之:use & require
相同: 都可以用来引用module(.PM). 不同: 1) 区别在于USE是在当前默认的@INC里面去寻找,一旦模块不在@INC中的话,用USE是不可以引入的,但是require可以指定路径: 2) ...