杀人游戏[tarjan]

传送门

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;
}

猜你喜欢

转载自blog.csdn.net/sslz_fsy/article/details/83591483