BZOJ-2438: [中山市选2011]杀人游戏(tarjan SCC 分解缩点+概率计算)
2438: [中山市选2011]杀人游戏
Time Limit: 10 Sec Memory Limit: 128 MBDescription
一位冷血的杀手潜入 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
分析:
首先,求一次强连通分量SCC。那么在一个SCC里知道了一个其他就都知道了。然后重新建图就得到一个有向无环图DAG。在这个DAG里,我们只要去询问那些入度为0的点即可。但是,有一种特殊情况,就是我们假设知道了n-1个人是或不是杀手,那么另外那一个人即便他的入度为0也是不需要询问的。这样的人存不存在我们只需要判断它的孩子的入度即可。若它的所有孩子的入度均大于1,也就是它的所有孩子都可由其他点遍历到,那么这个点就是不用询问的。而且这样的点即使有多个也只能算一次。
最终答案则显然为:
1.0-(询问人数/总人数)即可 [从中也可以看出要使询问人数尽可能少!]
#include<iostream> #include<cstdio> #include<algorithm> #include<cmath> #include<stack> using namespace std; int n,m,x,y,cnt,cntx,cntt=0,index,headx[300005],kkk[300005]; struct sdt { int to,nxt; }a[300005],aa[300005]; int head[300005],ind[300005],belong[300005],ans,sum[300005]; bool vis[300005],ff; struct bdq { int num,par; }b[300005]; stack<int>s; bool in_stack[300005]; void add(int fr,int tox) { a[++cnt].to=tox; a[cnt].nxt=head[fr]; head[fr]=cnt; } void addx(int fr,int tox) { aa[++cntt].to=tox; aa[cntt].nxt=headx[fr]; headx[fr]=cntt; } void tarjan(int x) { b[x].num=++index; b[x].par=index; s.push(x); in_stack[x]=1; vis[x]=1; for(int i=head[x];i;i=a[i].nxt) { if(!vis[a[i].to]) { tarjan(a[i].to); b[x].par=min(b[x].par,b[a[i].to].par); } else if(in_stack[a[i].to]) { b[x].par=min(b[x].par,b[a[i].to].num); } } if(b[x].par==b[x].num) { ++cntx; int k=0; do { k=s.top(); in_stack[k]=0; s.pop(); belong[k]=cntx; kkk[cntx]++; }while(x!=k); } } int main() { scanf("%d%d",&n,&m); for(int i=1;i<=m;i++) { scanf("%d%d",&x,&y); add(x,y); } for(int i=1;i<=n;i++) { if(!vis[i])tarjan(i); } for(int i=1;i<=n;i++) { for(int j=head[i];j;j=a[j].nxt) { if(belong[i]!=belong[a[j].to]) { addx(belong[i],belong[a[j].to]); ind[belong[a[j].to]]++; } } } for(int i=1;i<=cntx;i++) { if(ind[i])continue; ans++; if(ff)continue; if(kkk[i]!=1)continue; bool fv=0; for(int j=headx[i];j;j=aa[j].nxt) { if(ind[aa[j].to]<=1) { fv=1; break; } } if(!fv)ff=1; } if(ff)--ans; printf("%.6lf\n",(double)(n-ans)/n); return 0; }