【算法】Tarjan大锦集
Task1
Description
一位冷血的杀手潜入 Na-wiat,并假装成平民。警察希望能在 N 个人里面,
查出谁是杀手。
警察能够对每一个人进行查证,假如查证的对象是平民,他会告诉警察,他
认识的人, 谁是杀手, 谁是平民。 假如查证的对象是杀手, 杀手将会把警察干掉。
现在警察掌握了每一个人认识谁。
每一个人都有可能是杀手,可看作他们是杀手的概率是相同的。
问:根据最优的情况,保证警察自身安全并知道谁是杀手的概率最大是多
少?
Input
第一行有两个整数 N,M。
接下来有 M 行,每行两个整数 x,y,表示 x 认识 y(y 不一定认识 x,例如锦涛同志) 。
Output
仅包含一行一个实数,保留小数点后面 6 位,表示最大概率。
Sample Input
5 4
1 2
1 3
1 4
1 5
1 2
1 3
1 4
1 5
Sample Output
0.800000
HINT
警察只需要查证 1。假如1是杀手,警察就会被杀。假如 1不是杀手,他会告诉警
察 2,3,4,5 谁是杀手。而 1 是杀手的概率是 0.2,所以能知道谁是杀手但没被杀的概
率是0.8。对于 100%的数据有 1≤N ≤ 10 0000,0≤M ≤ 30 0000
#include<stdio.h> #include<stdlib.h> #include<algorithm> #include<iostream> #define il inline using namespace std; const int N=1000001; struct edge{int next,to;} e[N]; int n,M,m,scc,g[N],dfn[N],low[N],ins[N],s[N],bel[N],hav[N],ans,d[N],ind,top; il void addedge(int x,int y){ e[++M]=(edge){g[x],y};g[x]=M;d[y]++; } il void tarjan(int h){ dfn[h]=low[h]=(++ind); ins[h]=1;s[++top]=h; for(int i=g[h];i;i=e[i].next) if(!dfn[e[i].to]){ tarjan(e[i].to); low[h]=min(low[h],low[e[i].to]); } else if(ins[e[i].to]) low[h]=min(low[h],dfn[e[i].to]); if(low[h]==dfn[h]){ int now=0;scc++; while(h!=now){ now=s[top--]; bel[now]=scc; hav[scc]++; ins[now]=0; } } } il void rebuild(){ for(int i=1;i<=n;i++){ for(int j=g[i];j;j=e[j].next){ if(bel[i]!=bel[e[j].to]){ addedge(bel[i]+n,bel[e[j].to]+n); } } } } il bool chk(int h){ if(d[h]!=0||hav[h-n]!=1) return 0; for(int i=g[h];i;i=e[i].next){ if(d[e[i].to]==1) return 0; } return 1; } int main(){ scanf("%d%d",&n,&m); for(int i=1,x,y;i<=m;i++){ scanf("%d%d",&x,&y); addedge(x,y); } for(int i=1;i<=n;i++) if(!dfn[i]) tarjan(i); rebuild(); for(int i=1;i<=scc;i++) if(d[i+n]==0) ans++; for(int i=1;i<=scc;i++) if(chk(i+n)){ ans--;break; } printf("%.6lf",double(n-ans)/n); return 0; }
蜉蝣渴望着飞翔,尽管黄昏将至