bzoj2438:杀人游戏(强连通+特判)

该博客讨论了一道数学与图论结合的题目,涉及在有向图中如何以最小访问次数获取最多节点信息的问题。通过强连通组件分析和特定条件判断,找出解决问题的关键策略。首先进行强连通图的缩点,并记录每个联通块的点数和入度。接着,处理入度为0的联通块,它们至少需要被访问一次。最后,通过特判找到特殊点,该点的存在可以减少必要的访问次数。博主分享了实现思路和代码,强调了理解题意和特判的重要性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目传送门

题目大意:

 在有向图中,进行最少的访问,能到达(n-1)个点。求这个概率。

思路分析:

 如果题目能读懂的话,应该能看出来是强连通了,但是特判不是一般人能想到。

(n-1)就是特判的核心,因为只要知道(n-1)个点的信息,第n个点就不用问了~~神题吧~~~ 

具体思路:

 1、先跑强连通进行缩点(强连通的板子迟点发好不好),同时记录每个 联通块 含的点数(特判用),再开个ru数组记录入度。

2、入度为0的联通块,必须都要访问, ans+1(只要问这个块里的任意一个人,就已经知道全部人的信息);

3、特判阶段:看看能否找到那个唯一的特殊的点,

满足:(单点成联通块、无入度、无出度或者(出度指向的全部联通块都有其他入度))这三个条件,找到这个点,ans-1;

 4、输出此时的比例,完美。


上代码(我代码本来很笨很易懂的,为了打注解,调了很多缩进,sorry)

#include<cstdio>
const int mx=100005;
int n,m,len=0,ls=0,lb=0,l[mx],tou=0;
int ru[mx],bb[mx];//bb记录每个帮派有多少人(桶);//ru记录入度 
struct nod{int i,d,b,h,v;}a[mx];
struct nod1{int x,y,gg;}b[mx*10];
void ins(int x,int y) 
{
	len++;b[len].x=x; b[len].y=y; b[len].gg=a[x].h;a[x].h=len;
}
void dfs(int x)//强连通的核心代码
{
	a[x].i=a[x].d=++ls; l[++tou]=x;a[x].v=1;
	for(int i=a[x].h;i>0;i=b[i].gg)
	{
		int y=b[i].y;
		if(a[y].i==0)
		{
			dfs(y); if(a[y].d<a[x].d) a[x].d=a[y].d;
		}	
		else if(a[y].v==1)
		{
			if(a[y].i<a[x].d) a[x].d=a[y].i;
		}
	}
	if(a[x].i==a[x].d)
	{
		int k; lb++;
		while(1)
		{
			k=l[tou--];
			a[k].v=0; a[k].b=lb;
			bb[lb]++;//用来记录每个帮派有多少人 
			if(k==x) break;
		}
	}
}
bool ch(int x)//特判那个可爱的点
{
	if(bb[a[x].b]!=1||ru[a[x].b]!=0) //这个点要自己成派,入度为0 
		return 0;
	for(int i=a[x].h;i>0;i=b[i].gg)
	{
		int y=b[i].y;
		if(ru[a[y].b]==1) return 0;//x能到的帮派,要有其他入度 
	}
	return 1;
}
int main()
{
	scanf("%d %d",&n,&m);
	for(int i=1;i<=n;i++) bb[i]=a[i].h=a[i].v=a[i].i=a[i].d=0;
	int x,y;
	for(int i=1;i<=m;i++) { scanf("%d %d",&x,&y); ins(x,y); }
	for(int i=1;i<=n;i++) { if(a[i].i==0) dfs(i); }//跑强连通 

	for(int i=1;i<=lb;i++) ru[i]=0;
	for(int i=1;i<=len;i++)//扫边 记录入度 
	{
		if(a[b[i].x].b!=a[b[i].y].b)//x,y不在同一个帮派
		{
			ru[a[b[i].y].b]++;// y 所在帮派的入度+1 
		}
	}
	int ans=0;//计算有机会被杀死的提问次数 
	for(int i=1;i<=lb;i++) //入度为0的那群人,肯定没人认识,要问 
		if(ru[i]==0) ans++;//因为要问,所以有机会被杀死 
	for(int i=1;i<=n;i++) //特判 那个孤独的点 
		if(ch(i)==1) { ans--;break; }
	printf("%.6lf\n",double(n-ans)/double(n));
	return 0;
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值