算法:tarjan缩点
难度:NOIP 细节
题解:首先,易证,如果有环,只需查证其中的一个点即可,所以首先用tarjan缩点
然后,必须查证的人的个数等于入度为0的强连通分量个数!
但是,这样并不能AC,我们考虑下面这种情况:
感谢 GXZlegend 提供的图片
这时只需要查证2/1即可,可以不查证1/2
当一个入度为0的点,其中元素为1,而它的出边所到的点的入度都>1,则ans--。 因为它们可以被别的点更新。
最后 (n-ans)/n ->即为存活概率。
时间复杂度
注意:m=0的时候需要特判!!!
注意:如果一共只有3个人,已知2个人的信息,则第三个人可以不查证。
所以如果
input: 3 1
1 2
output:0.666667
所以,在找不需要查证的人的时候,如果他与任何人都没有连边也需要判断!
代码如下:
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <iostream>
#include <cmath>
#include <cstdlib>
#include <queue>
#define ll long long
#define N 100005
#define M 300005
using namespace std;
struct node
{
int next;
int to;
int val;
}edge[M];
struct nod
{
int next;
int to;
int val;
}edg[M];
int head[N],hea[N];
int dfn[N],low[N],my_stack[N];
int vis[N],posi[N];
int dep,cnt,top;
int scr_cnt,scr_num[N],inr[N];
int ctt,ctq;
void init()
{
dep=0;
cnt=0;
top=0;
scr_cnt=0;
ctt=1;
ctq=1;
memset(hea,-1,sizeof(hea));
memset(dfn,0,sizeof(dfn));
memset(low,0,sizeof(low));
memset(head,-1,sizeof(head));
}
void add(int u,int v)
{
edge[ctt].next=head[u];
edge[ctt].to=v;
head[u]=ctt++;
}
void add2(int u,int v)
{
edg[ctq].next=hea[u];
edg[ctq].to=v;
hea[u]=ctq++;
}
void tarjan(int rt)
{
dfn[rt]=low[rt]=++dep;
my_stack[++top]=rt;
for(int i = head[rt];i != -1;i=edge[i].next)
{
int to=edge[i].to;
if(!dfn[to])
{
tarjan(to);
low[rt]=min(low[to],low[rt]);
}else if(!posi[to])
{
low[rt]=min(low[rt],dfn[to]/*刚刚写成了dfn[rt]*/);
}
}
if(dfn[rt]==low[rt])
{
scr_cnt++;
int v=-1;
int t=0;
while(v!=rt)
{
v=my_stack[top];
top--;
t++;
posi[v]=scr_cnt;
}
scr_num[scr_cnt]=t;
}
}
int main()
{
int n,m;
scanf("%d%d",&n,&m);
if(m==0)
{
printf("%.6lf\n",1.0/n);
exit(0);
}/*else if(n==1)
{
puts("1.000000");
exit(0);
}*/
init();
for(int i = 1;i <= m;i++)
{
int x,y;
scanf("%d%d",&x,&y);
add(x,y);
}
for(int i = 1;i <= n;i++)
{
if(dfn[i]==0)
{
tarjan(i);
}
}
for(int i = 1;i <= n;i++)
{
for(int j = head[i];j != -1;j=edge[j].next)
{
int to=edge[j].to;
if(posi[to]!=posi[i])
{
inr[posi[to]]++;
add2(posi[i],posi[to]);
}
}
}
int ans=0,fla=0;
for(int i = 1;i <= scr_cnt;i++)
{
if(!inr[i]) ans++;
}
if(ans!=1)
{
for(int i = 1;i <= scr_cnt;i++)
{
int pp=0;
if(scr_num[i]==1&&inr[i]==0)
{
int u=0;
for(int j = hea[i];j != -1;j=edg[j].next)
{
u=1;
int to=edg[j].to;
if(inr[to]>1) pp=1;
else
{
pp=0;
break;
}
}
if(pp||(u==0))
{
fla=1;
break;
}
}
}
if(fla) ans--;
}
double tmp=(1.0*(n-ans))/(n);
printf("%.6lf",tmp);
return 0 ;
}
/*
6 10
1 2
1 4
2 5
2 3
3 1
4 6
4 3
5 3
5 4
6 2
*/