Day7 割点、割边和强连通分量

时间:2019-02-16
本文章向大家介绍Day7 割点、割边和强连通分量,主要包括Day7 割点、割边和强连通分量使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

A.【dfn】交换机 割点 模板

题目

题目描述
n个城市之间有通讯网络,每个城市都有通讯交换机,直接或间接与其它城市连接。因电子设备容易损坏,需给通讯点配备备用交换机。
但备用 交换机数量有限,不能全部配备,只能给部分重要城市配置。
于是规定:如果某个城市由于交换机损坏,不仅本城市通讯中断,还造成其它城市通讯中断,则配备备 用交换机。
请你根据城市线路情况,计算需配备备用交换机的城市个数,及需配备备用交换机城市的编号。
友情提示:图论常见的坑点,重边,自环,还有对本题来说的不连通
输入格式
第一行,一个整数n,表示共有n个城市(2<=n<=20000)
下面有若干行(<=60000):每行2个数a、b,a、b是城市编号,表示a与b之间有直接通讯线路。
输出格式
第一行,1个整数m,表示需m个备用交换机。
下面有m行,每行有一个整数,表示需配备交换机的城市编号。
输出顺序按编号由小到大。如果没有城市需配备备用交换机则输出0。
样例数据
input
7
1 2
2 3
2 4
3 4
4 5
4 6
4 7
5 6
6 7
output
2
2
4
数据规模与约定
gdoi
时间限制:1s
空间限制:256MB

错误原因

1.局部变量ansans未初始化为00
2.数据范围开小了。

代码

#include<bits/stdc++.h>
using namespace std;
const int maxn=6e4+10;
template<typename T>inline void read(T &x)
{
	x=0;
	T f=1,ch=getchar();
	while (!isdigit(ch)) ch=getchar();
	if (ch=='-') f=-1, ch=getchar();
	while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48), ch=getchar();
	x*=f;
}
int ver[maxn<<1],Next[maxn<<1],head[maxn],len;
inline void add(int x,int y)
{
	ver[++len]=y,Next[len]=head[x],head[x]=len;
}
int dfn[maxn],low[maxn],id,root;
bool cut[maxn];
inline void tarjan(int x)
{
	int tot=0;
	low[x]=dfn[x]=++id;
	for (int i=head[x];i;i=Next[i])
	{
		int y=ver[i];
		if (!dfn[y])
		{
			tarjan(y);
			low[x]=min(low[x],low[y]);//更新当前节点的low值
			if (low[y]>=dfn[x])
			{
				++tot;
				if (x^root || tot>1) cut[x]=1;
			}
		}
		else
			low[x]=min(low[x],dfn[y]);
	}
}
int main()
{
	freopen("gd.in","r",stdin);
	freopen("gd.out","w",stdout);
	int n,x,y,ans=0;read(n);
	while (scanf("%d %d",&x,&y)==2)
	{
		if (x==y) continue;
		add(x,y),add(y,x);
	}
	for (int i=1;i<=n;++i)
		if (!dfn[i]) root=i,tarjan(i);
	for (int i=1;i<=n;++i)
		if (cut[i]) ++ans;
	printf("%d\n",ans);//割点的总数
	for (int i=1;i<=n;++i)
		if (cut[i])
			printf("%d\n",i);//输出割点
	return 0;
}

B. [zjoi2004]嗅探器——dfnlow经典题目 割点

题目p1693

LUOGU 5058

描述 Description
某军搞信息对抗实战演习.红军成功地侵入了蓝军的内部网络.蓝军共有两个信息中心.红军计划在某台中间服务器上安装一个嗅探器,从而能够侦听到两个信息中心互相交换的所有信息.但是蓝军的网络相当的庞大,数据包从一个信息中心传到另一个信息中心可以不止有一条通路.现在需要你尽快地解决这个问题.应该把嗅探器安装在哪个中间服务器上才能保证所有的数据包都能被捕获?
输入格式 Input Format
第一行一个整数n(1<=n<=100000),表示蓝军网络中服务器的数目.
接下来若干行是对蓝军网络的拓扑结构描述.每行是两个整数i,j表示编号为I和编号为j的两台服务器间存在连接(显然连接是双向的).服务器的编号从1开始.描述一两个0结束.再接下来一行是两个整数a,b分别表示两个中心服务器的编号.
输出格式 Output Format
如果有多个解输出编号最小的一个.如果找不到任何解,输出”No solution”.
样例输入 Sample Input
5
2 1
2 5
1 4
5 3
2 3
5 1
0 0
4 2
样例输出 Sample Output
1
时间限制 Time Limitation
1s
注释 Hint
部分小数据
来源 Source
zjoi2004
数据加强来自 2017届 杨晓虎 岳川梦真

题解

首先,这个点一定是一个割点。
st然后我们可以考虑s与t必经的割点。
xy3如果有一个x点,他连接了一个y点,那么我们还可以总结出3个条件:
1.x1.x不是起点或终点,因为题目要求在中间服务器上建嗅探器。
2.x2.x是割点。
3.tdfnydfny访3.终点t的dfn应该大于等于y点的dfn,因为要确保终点在y点或之后被访问到,
x即x点为必经的点。
4.tlowxdfnx4.终点t的low应该大于等于x点的dfn,因为要确保终点必须要经过x点。

代码

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+10;
template<typename T>inline void read(T &x)
{
	x=0;
	T f=1,ch=getchar();
	while (!isdigit(ch)) ch=getchar();
	if (ch=='-') f=-1, ch=getchar();
	while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48), ch=getchar();
	x*=f;
}
int ver[maxn<<1],Next[maxn<<1],head[maxn],len;
inline void add(int x,int y)
{
	ver[++len]=y,Next[len]=head[x],head[x]=len;
}
int s,t,ans=0x3f3f3f3f;
int dfn[maxn],low[maxn],id;
inline void tarjan(int x,int root)
{
	dfn[x]=low[x]=++id;
	for (int i=head[x];i;i=Next[i])
	{
		int y=ver[i];
		if (!dfn[y])
		{
			tarjan(y,x);
			low[x]=min(low[x],low[y]);
			if (x!=s && low[y]>=dfn[x] && dfn[t]>=dfn[y] && low[t]>=dfn[x])
				ans=min(ans,x);
		}
		else if (y!=root)
			low[x]=min(low[x],dfn[y]);
	}
}
int main()
{
	freopen("dfnIn.in","r",stdin);
	freopen("dfnin.out","w",stdout);
	int n;read(n);
	while (1)
	{
		int x,y;
		read(x);read(y);
		if (!x && !y) break;
		add(x,y),add(y,x);
	}
	read(s);read(t);
	tarjan(s,0);
	if (ans>n) printf("No solution\n");
	else printf("%d\n",ans);
	return 0;
}

C. BZOJ 1123: [POI2008]BLO 割点

题目

BZOJ 1123
LUOGU 3469

错误原因

ans[]ans[]未开longlonglong long

代码

#include<bits/stdc++.h>
using namespace std;
const int maxn=5e5+10;
template<typename T>inline void read(T &x)
{
	x=0;
	T f=1,ch=getchar();
	while (!isdigit(ch)) ch=getchar();
	if (ch=='-') f=-1, ch=getchar();
	while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48), ch=getchar();
	x*=f;
}
int ver[maxn<<1],Next[maxn<<1],head[maxn],len;
inline void add(int x,int y)
{
	ver[++len]=y,Next[len]=head[x],head[x]=len;
}
int dfn[maxn],low[maxn],id,n,m;
int cut[maxn],siz[maxn];
long long ans[maxn];
inline void tarjan(int x)
{
	dfn[x]=low[x]=++id;
	siz[x]=1;
	int tot=0,sum=0;
	for (int i=head[x];i;i=Next[i])
	{
		int y=ver[i];
		if (!dfn[y])
		{
			tarjan(y);
			siz[x]+=siz[y];//计算以x为跟的子树的大小
			low[x]=min(low[x],low[y]);
			if (low[y]>=dfn[x])//割点判定法则
			{
				++tot;
				ans[