题目大意:
在有向图中,进行最少的访问,能到达(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;
}