BZOJ-2438: [中山市选2011]杀人游戏(tarjan SCC 分解缩点+概率计算)

2438: [中山市选2011]杀人游戏

Time Limit: 10 Sec  Memory Limit: 128 MB

Description

一位冷血的杀手潜入 Na-wiat,并假装成平民。警察希望能在 N 个人里面,
查出谁是杀手。 
警察能够对每一个人进行查证,假如查证的对象是平民,他会告诉警察,他
认识的人, 谁是杀手, 谁是平民。 假如查证的对象是杀手, 杀手将会把警察干掉。 
现在警察掌握了每一个人认识谁。 
每一个人都有可能是杀手,可看作他们是杀手的概率是相同的。 
问:根据最优的情况,保证警察自身安全并知道谁是杀手的概率最大是多
少?

Input

第一行有两个整数 N,M。 
接下来有 M 行,每行两个整数 x,y,表示 x 认识 y(y 不一定认识 x,例如***同志) 。

Output

仅包含一行一个实数,保留小数点后面 6 位,表示最大概率。

Sample Input

5 4
1 2
1 3
1 4
1 5

Sample Output

0.800000

HINT

 

警察只需要查证 1。假如1是杀手,警察就会被杀。假如 1不是杀手,他会告诉警

察 2,3,4,5 谁是杀手。而 1 是杀手的概率是 0.2,所以能知道谁是杀手但没被杀的概

率是0.8。对于 100%的数据有 1≤N ≤  10 0000,0≤M ≤  30 0000

 

分析:

首先,求一次强连通分量SCC。那么在一个SCC里知道了一个其他就都知道了。然后重新建图就得到一个有向无环图DAG。在这个DAG里,我们只要去询问那些入度为0的点即可。但是,有一种特殊情况,就是我们假设知道了n-1个人是或不是杀手,那么另外那一个人即便他的入度为0也是不需要询问的。这样的人存不存在我们只需要判断它的孩子的入度即可。若它的所有孩子的入度均大于1,也就是它的所有孩子都可由其他点遍历到,那么这个点就是不用询问的。而且这样的点即使有多个也只能算一次。

最终答案则显然为:

1.0-(询问人数/总人数)即可 [从中也可以看出要使询问人数尽可能少!]

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<stack>
using namespace std;

int n,m,x,y,cnt,cntx,cntt=0,index,headx[300005],kkk[300005];
struct sdt
{
	int to,nxt;
}a[300005],aa[300005];
int head[300005],ind[300005],belong[300005],ans,sum[300005];
bool vis[300005],ff;
struct bdq
{
	int num,par;
}b[300005];
stack<int>s;
bool in_stack[300005];

void add(int fr,int tox)
{
	a[++cnt].to=tox;
	a[cnt].nxt=head[fr];
	head[fr]=cnt;
}

void addx(int fr,int tox)
{
	aa[++cntt].to=tox;
	aa[cntt].nxt=headx[fr];
	headx[fr]=cntt;
}

void tarjan(int x)
{
	b[x].num=++index;
	b[x].par=index;
	s.push(x);
	in_stack[x]=1;
	vis[x]=1;
	
	for(int i=head[x];i;i=a[i].nxt)
	{
		if(!vis[a[i].to])
		{
			tarjan(a[i].to);
			b[x].par=min(b[x].par,b[a[i].to].par);
		}
		else if(in_stack[a[i].to])
		{
			b[x].par=min(b[x].par,b[a[i].to].num);
		}
	}
	if(b[x].par==b[x].num)
	{
		++cntx;
		int k=0;
		do
		{
			k=s.top();
			in_stack[k]=0;
			s.pop();
			belong[k]=cntx;
			kkk[cntx]++;
		}while(x!=k);
	}
}

int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++)
	{
		scanf("%d%d",&x,&y);
		add(x,y);
	}
	for(int i=1;i<=n;i++)
	{
		if(!vis[i])tarjan(i);
	}
	
	for(int i=1;i<=n;i++)
	{
		for(int j=head[i];j;j=a[j].nxt)
		{
			if(belong[i]!=belong[a[j].to])
			{
				addx(belong[i],belong[a[j].to]);
				ind[belong[a[j].to]]++;
			}
		}
	}
	for(int i=1;i<=cntx;i++)
	{
		if(ind[i])continue;
		ans++;
		if(ff)continue;
		if(kkk[i]!=1)continue;
		bool fv=0;
		for(int j=headx[i];j;j=aa[j].nxt)
		{
			if(ind[aa[j].to]<=1)
			{
				fv=1;
				break;
			}
		} 
		if(!fv)ff=1;
	}
	
	if(ff)--ans;
	printf("%.6lf\n",(double)(n-ans)/n);
	return 0;
}

  

posted @ 2017-04-19 21:17  winmt  阅读(129)  评论(0编辑  收藏
当前时间: