洛谷 4819 [中山市选]杀人游戏
题目戳这里
Solution
首先我们可以想到,如果某个人没有任何一个人认识他,那么警察必须通过调查他获取他的身份,那么思路便很清晰了,从所有入度为0的点遍历,就可以获得所有人的身份,并且易知这样的花费也是最少的,但如果这张图就是一个环呢?那么不是没有一个入度为0的点?所以我们需要通过缩点来使这张图变成DAG,记录所有入度为0的点除以总点数。
然后...你就会发现只有21分,233。
为什么呢?举个栗子:
假设你通过其他的点把整张图遍历完了,只剩下一个点,那么我们还需要对他遍历吗?因为只有一个杀手,所以很明显通过其他人的身份就知道这个人的身份,所以这种情况需要特判。
但这个点满足什么条件呢?很明显不需要它也可以把其他点遍历完,要么是他不连接任何点,要么是它连接的点度数都大于1(通过其他点也可以到达)。这样就可以愉快的AC了!
PS:数组真的要开大点,很坑!!!
Coding
#include<bits/stdc++.h>
using namespace std;
const int N = 1e6+10;
struct road
{
int to,next;
}e[N*5],edge[N*5];
int n,m,head[N],cnt,u[N],v[N],in[N];
void add(int x,int y)
{
e[++cnt].to=y;
e[cnt].next=head[x];
head[x]=cnt;
}
void Readd(int x,int y)
{
edge[++cnt].to=y;
edge[cnt].next=head[x];
head[x]=cnt;
}
int sum,color[N],low[N],ins[N],Time[N],sta[N],top=1,col,num[N];
void Tarjan(int x)
{
Time[x]=low[x]=++sum;
sta[top++]=x;
ins[x]=1;
for(int i=head[x];i;i=e[i].next)
{
int v=e[i].to;
if(ins[v]==0)
{
Tarjan(v);
low[x]=min(low[x],low[v]);
}
else if(ins[v]==1) low[x]=min(low[x],Time[v]);
}
if(Time[x]==low[x])
{
col++;
do
{
top--;
color[sta[top]]=col;
ins[sta[top]]=-1;
}while(sta[top]!=x);
}
return ;
}
int main()
{
cin>>n>>m;
if(n==1) {cout<<"1.000000"; return 0;}
if(m==0) {printf("%.6lf",1.0/n); return 0;}
for(int i=1;i<=m;i++)
{
scanf("%d%d",&u[i],&v[i]);
add(u[i],v[i]);
}
for(int i=1;i<=n;i++)
if(!ins[i]) Tarjan(i);
memset(head,0,sizeof(head));
cnt=0;
for(int i=1;i<=n;i++)
num[color[i]]++;
for(int i=1;i<=m;i++)
if(color[u[i]]!=color[v[i]]) Readd(color[u[i]],color[v[i]]),in[color[v[i]]]++;
int flag=0,ans=0;
for(int x=1;x<=col;x++)
{
int fuck=1;
if(in[x]!=0) continue ;
if(num[x]!=1) continue ;
for(int i=head[x];i;i=edge[i].next)
if(in[edge[i].to]==1) {fuck=0; break ;}
if(fuck==1)
{
flag=1;
break ;
}
}
for(int i=1;i<=col;i++)
if(in[i]==0) ans++;
ans-=flag;
double answer=double(n-ans)/double(n);
printf("%.6lf",answer);
return 0;
}