#Tarjan#洛谷 4819 [中山市选]杀人游戏 分析 代码

题目


缩点后显然只考虑入度为0的点的个数,
但是问题是如果有一个入度为0的点缩点前只有1个点
且它的出边上的所有点都可以被其它入度为0的点遍历,
那么可以将其它点全部排除后剩下的这个点就是凶手,概率要加1


代码

#include <cstdio>
#include <cctype>
#include <map>
#include <stack>
#define rr register
using namespace std;
const int N=100011; struct node{int y,next;}e[N*5],E[N*5]; stack<int>stac; map<int,bool>uk[N];
int dfn[N],low[N],v[N],col[N],deg[N],as[N],hs[N],siz[N],m,ans,flag,Et,tot,cnt,n;
inline signed iut(){
	rr int ans=0,f=1; rr char c=getchar();
	while (!isdigit(c)) f=(c=='-')?-f:f,c=getchar();
	while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
	return ans*f;
}
inline signed min(int a,int b){return a<b?a:b;} 
inline void tarjan(int x){
	dfn[x]=low[x]=++tot,
	stac.push(x),v[x]=1;
	for (rr int i=as[x];i;i=e[i].next)
	if (!dfn[e[i].y]){
		tarjan(e[i].y);
		low[x]=min(low[x],low[e[i].y]);
	}else if (v[e[i].y])
	    low[x]=min(low[x],dfn[e[i].y]);
	if (dfn[x]==low[x]){
		rr int y; ++cnt;
		do{
			y=stac.top(),stac.pop(),
			col[y]=cnt,v[y]=0,++siz[cnt];
		}while (x^y);
	}
}
signed main(){
	n=iut(),m=iut();
	for (rr int i=1;i<=m;++i){
		rr int x=iut(),y=iut();
		e[i]=(node){y,as[x]},as[x]=i;
	}
	for (rr int i=1;i<=n;++i)
	    if (!dfn[i]) tarjan(i);
	for (rr int i=1;i<=n;++i)
	for (rr int j=as[i];j;j=e[j].next)
	if (col[i]^col[e[j].y]){
		if (uk[col[i]][col[e[j].y]]) continue;
		uk[col[i]][col[e[j].y]]=1;
		E[++Et]=(node){col[e[j].y],hs[col[i]]},
		    hs[col[i]]=Et,++deg[col[e[j].y]];
	}
	for (rr int i=1;i<=cnt;++i)
	if (!deg[i]){
		rr bool f=1; ++ans;
		if (siz[i]>1) continue;
		for (rr int j=hs[i];j;j=E[j].next)
		    if (deg[E[j].y]==1){
		    	f=0;
		    	break;
			}
		if (f) flag=1;
	}
	return !printf("%lf",(n-ans+flag)*1.0/n);
}