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& ...
随机推荐
- 安卓&IOS 手机添加O365 邮箱账户
手机添加O365 邮件账户 一.Android手机添加O365邮件账户 1. 找到手机上“电子邮件” 2. 打开设置 3. 点击添加账户 4. 选择“Exchange” 5. 输入O365的邮箱账户和 ...
- 利用JSTL重写查询的jsp页面
利用JSTL重写Java Web MVC实例中的jsp页面 第一步:导入jstl.jar和standard.jar文件
- 用户授权policy
定义策略类 php artisan make:policy PostPolicy app/Policies/PostPolicy.php public function update(User $us ...
- ZigBee cc2530芯片学习 error记录(1)
ZigBee cc2530芯片学习 error记录 Error[e46]: Undefined external "LcdInit" referred in main( xxx ...
- SQL Server2012 T-SQL对分页的增强尝试
简介 SQL Server 2012中在Order By子句之后新增了OFFSET和FETCH子句来限制输出的行数从而达到了分页效果.相比较SQL Server 2005/2008的ROW_Numbe ...
- Robot Framework(十四) 扩展RobotFramework框架——创建测试库
4.1创建测试库 Robot Framework的实际测试功能由测试库提供.有许多现有的库,其中一些甚至与核心框架捆绑在一起,但仍然经常需要创建新的库.这个任务并不复杂,因为正如本章所示,Robot ...
- 【转】《windows核心编程》读书笔记
这篇笔记是我在读<Windows核心编程>第5版时做的记录和总结(部分章节是第4版的书),没有摘抄原句,包含了很多我个人的思考和对实现的推断,因此不少条款和Windows实际机制可能有出入 ...
- 【转】C#的版本
这年头啥东东都喜欢过段时间整个啥新版本出来.汽车,手机如此,软件就更是如此了啊.比如啥Iphone 4,Iphone 5,Windows 8,Oracle 12C,SQL Server 2010. 版 ...
- C# 关于datetime的用法(网上考的)
实例: 用户输入一个日期,要求输出这个日期是星期几和在这一年中的第几天: 复制代码代码如下: //声明一个DateTime类型的变量用于存放用户输入的日期DateTime dt;Console.Wri ...
- [LOJ] #2363「NOIP2016」愤怒的小鸟
精度卡了一个点,别人自带大常数,我自带大浮点误差qwq. 听了好几遍,一直没动手写一写. f[S]表示S集合中的猪被打死的最少抛物线数,转移时考虑枚举两个点,最低位的0为第一个点,枚举第二个点,构造一 ...