tarjan缩点,找入度为0的点(因为必须要查)的个数
同时如果有一个点入度为0,且它连如的点入度不为1,那么把它放在最后查,就可以少查一次
#include<bits/stdc++.h>
#define N 100005
#define M 300005*2
using namespace std;
int first[N],next[M],to[M],tot;
int dfn[N],low[N],sta[N],insta[N];
int id[N],siz[N],sign,top,cnt;
int n,m,du[N],ans,flag;
map<pair<int,int>,int >S;
vector<int> V[N];
int read(){
int cnt=0,f=1;char ch=0;
while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();}
while(isdigit(ch))cnt=cnt*10+(ch-'0'),ch=getchar();
return cnt*f;
}
void add(int x,int y){
next[++tot]=first[x],first[x]=tot,to[tot]=y;
}
void dfs(int u){
dfn[u]=low[u]=++sign;
sta[++top]=u,insta[u]=1;
for(int i=first[u];i;i=next[i]){
int t=to[i];
if(!dfn[t]) dfs(t),low[u]=min(low[u],low[t]);
else if(insta[t] && dfn[t]<low[u]) low[u]=dfn[t];
}
if(dfn[u]==low[u]){
cnt++; do{
id[sta[top]]=cnt; siz[cnt]++;
insta[sta[top]]=0;
}while(sta[top--]!=u);
}
}
int main(){
n=read(),m=read();
for(int i=1;i<=m;i++){
int x=read(),y=read();
add(x,y);
}
for(int i=1;i<=n;i++)
if(!dfn[i]) dfs(i);
for(int i=1;i<=n;i++)
for(int j=first[i];j;j=next[j])
if(id[i]!=id[to[j]]){
du[id[to[j]]]++;
if(!S[make_pair(id[i],id[to[j]])]){
V[id[i]].push_back(to[j]);
S[make_pair(id[i],id[to[j]])]=1;
}
}
for(int i=1;i<=cnt;i++){
if(!flag && du[i]==0 && siz[i]==1){
int pd=0;
for(int j=0;j<V[i].size();j++)
if(du[V[i][j]]==1) pd=1;
if(!pd) flag=1;
}
if(du[i]==0) ans++;
}
double Ans=1-double((ans-flag)*1.0)/n;
printf("%0.6lf\n",Ans); return 0;
}