您的位置:首页 > 其它

[bzoj 2438][中山市选2011]杀人游戏 概率+tarjan

2017-07-27 20:45 423 查看
考试的时候想了很多,不知道它那个概率究竟是怎么算。。没想到能蒙30分。rp爆发hhh

题解转自不知道哪里来的老师发的(代码出自自己)。

实际上警察就是两种结果——查到犯人或死亡,而死亡事件一定是包含在“调查未知身份的人”,也就是说调查未知身份的人越多,死亡概率就越高,所以我们要求的问题就是警察要尽可能少调查未知身份的人 

给出公式 P(死亡)=调查的未知身份的人数/总人数 

“死亡”和”存活”是对立事件,而”存活”等价于”找到犯人” 所以 ans=1−P死亡 问题转化为求调查最少的未知身份人的人数 

同时我们发现,对于一个环(1认识2,2认识3,3认识1),我们是可以把他们看作一个人的,也就是说,只要调查了他们其中一个,其他两个我们一定可以安全到达(知道他是犯人或平民) 对于这个问题,我们使用tarjan对图进行缩点处理,然后我们所要调查的对象就是重构图中那些入度为0的点(没有人知道这些点里的人的身份),调查的未知身份的人数就是这些点的数量 (如果其中入度不为0,那么我们一定可以从一个入度为0的点走到这里,即我们调查这个点时是知道这里面的人的身份的) 

注意: 

有一个特殊情况,如果说至少有一个点,它只有一个人,他是完全孤立的(即没有人知道这个人的身份,他对外面的世界也一无所知)或者他认识的每个人都被除他以外的其他人所认识(即它所能到达的点的入度>1),那么我们在安全调查完除他以外的其他人后,他一定是杀手(但在图上他属于未知身份的人),我们不必去调查他,直接抓起来就可以了,所以调查未知身份的人数要– 1(我们可以想得简单些,比如n=1时这个人一定是杀手,即P存活=1) 

#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
#define pos(i,a,b) for(int i=(a);i<=(b);i++)
#define N 500000
int n,m;
int low
,dfn
,stack
,instack
,belong
;
int peo
;
struct haha{
int next,to;
};
haha edgechu
,edge
;
int head
,cnt=1,cntchu=1,headchu
;
void add(int u,int v){
edge[cnt].to=v;
edge[cnt].next=head[u];
head[u]=cnt++;
}
void addchu(int u,int v){
edgechu[cntchu].to=v;
edgechu[cntchu].next=headchu[u];
headchu[u]=cntchu++;
}
int ji=1,hea,cn=1;
void tarjan(int now){
low[now]=dfn[now]=ji++;
stack[++hea]=now;
instack[now]=1;
for(int i=headchu[now];i;i=edgechu[i].next){
int to=edgechu[i].to;
if(dfn[to]==-1){
tarjan(to);
low[now]=min(low[now],low[to]);
}
else{
if(instack[to]){
low[now]=min(low[now],dfn[to]);
}
}
}
if(low[now]==dfn[now]){
int temp;
while(1){
temp=stack[hea--];
belong[temp]=cn;
instack[temp]=0;
peo[cn]++;
if(temp==now)
break;
}
cn++;
}
}
int in
,out
;
int ans;
int flag
;
int main(){
freopen("killer.in","r",stdin);
freopen("killer.out","w",stdout);
scanf("%d%d",&n,&m);
memset(dfn,-1,sizeof(dfn));
pos(i,1,m){
int x,y;
scanf("%d%d",&x,&y);
addchu(x,y);
}
pos(i,1,n){
if(dfn[i]==-1)
tarjan(i);
}
cn--;
pos(i,1,n){
for(int v=headchu[i];v;v=edgechu[v].next){
int j=edgechu[v].to;
if(belong[i]!=belong[j]){
add(belong[i],belong[j]);
in[belong[j]]++;
out[belong[i]]++;
}
}
}
pos(i,1,cn){
if(in[i]==0){
ans++;
}
}
pos(i,1,cn){
if(peo[i]==1){
if(in[i]==0&&out[i]==0){
ans--;
double temp=(double)(n-ans)/n;
printf("%0.6lf",temp);
return 0;
}
else{
for(int j=head[i];j;j=edge[j].next){
int to=edge[j].to;
if(in[to]>1){
flag[i]++;
}
}
if(flag[i]==out[i]&&(flag[i])){
ans--;
double temp=(double)(n-ans)/n;
printf("%0.6lf",temp);
return 0;
}
}
}
}
double temp=(double)(n-ans)/n;
printf("%0.6lf",temp);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  线段树