bzoj2438: [中山市选2011]杀人游戏
2438: [中山市选2011]杀人游戏
Description
一位冷血的杀手潜入 Na-wiat,并假装成平民。警察希望能在 N 个人里面,
查出谁是杀手。
警察能够对每一个人进行查证,假如查证的对象是平民,他会告诉警察,他
认识的人, 谁是杀手, 谁是平民。 假如查证的对象是杀手, 杀手将会把警察干掉。
现在警察掌握了每一个人认识谁。
每一个人都有可能是杀手,可看作他们是杀手的概率是相同的。
问:根据最优的情况,保证警察自身安全并知道谁是杀手的概率最大是多
少?
Input
第一行有两个整数 N,M。
接下来有 M 行,每行两个整数 x,y,表示 x 认识 y(y 不一定认识 x,例如*,*,*,同志) 。
Output
仅包含一行一个实数,保留小数点后面 6 位,表示最大概率。
Sample Input
1 2
1 3
1 4
1 5
Sample Output
HINT
警察只需要查证 1。假如1是杀手,警察就会被杀。假如 1不是杀手,他会告诉警
察 2,3,4,5 谁是杀手。而 1 是杀手的概率是 0.2,所以能知道谁是杀手但没被杀的概
率是0.8。对于 100%的数据有 1≤N ≤ 10 0000,0≤M ≤ 30 0000
刚刚接触tarjan算法……说是之后某神犇会出tarjan的题orz写思路前一定要多说一句QAQ写tarjan函数的时候,
for (int i=head[x];i;i=e[i].next)
{
int y=e[i].v;
if (!dfn[y])
{
tarjan(y);
low[x]=min(low[x],low[y]);
}
else if (vis[y])
{
low[x]=min(dfn[y],low[x]);
}
}
这个千万要注意……查了一个晚上一个中午的错orz好气。vis记录的是是否在栈中,而不是是否遍历过,所以要用dfn来判断,所以当vis为0时此点其实可能已经搜过了……
最开始看到概率就很懵逼,样例也比较水,就翻了dalao的题解看了下解释……
因为询问一个人只有两种可能,一种此人是杀手,一种此人不是。所以概率即为:(1-询问人数/总人数)
那么要求最大概率,就要使询问人数最少。这个时候思路就很明显了,先tarjan缩点,然后求出入度为0的点的个数(必须要询问)。
这个时候还有一种特殊情况,某个入度为0的点,它所连向的所有点得入度都>1,即我们确实是不需要询问这个点就可以知道答案的。所以就减去即可。
代码如下ovo
1 #include<iostream> 2 #include<cstdio> 3 #include<cstdlib> 4 #include<cstring> 5 #include<string> 6 #include<cmath> 7 #include<algorithm> 8 #include<queue> 9 using namespace std; 10 int read() 11 { 12 int f=1,p=0; 13 char c=getchar(); 14 while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();} 15 while (c>='0'&&c<='9') {p=p*10+c-'0';c=getchar();} 16 return f*p; 17 } 18 struct edge 19 { 20 int next,u,v; 21 }e[300005],nw[300005]; 22 int tot=0,s=0,n,m,sum=0,res=0.0; 23 int st[300005],vis[100005],num[300005],be[300005],head[300005],enter[300005],hd[300005],low[100005],dfn[100005]; 24 void tarjan(int x) 25 { 26 low[x]=dfn[x]=++tot; 27 vis[x]=1; 28 st[++s]=x; 29 for (int i=head[x];i;i=e[i].next) 30 { 31 int y=e[i].v; 32 if (!dfn[y]) 33 { 34 tarjan(y); 35 low[x]=min(low[x],low[y]); 36 } 37 else if (vis[y]) 38 { 39 low[x]=min(dfn[y],low[x]); 40 } 41 } 42 if (low[x]==dfn[x]) 43 { 44 while (st[s+1]!=x) 45 { 46 num[x]++; 47 be[st[s]]=x; 48 vis[st[s]]=0; 49 s--; 50 } 51 } 52 } 53 int main() 54 { 55 n=read();m=read(); 56 for (int i=1;i<=m;i++) 57 { 58 int u=read(),v=read(); 59 e[i].u=u; 60 e[i].v=v; 61 e[i].next=head[u]; 62 head[u]=i; 63 } 64 for (int i=1;i<=n;i++) 65 { 66 if (!dfn[i]) tarjan(i); 67 } 68 for (int i=1;i<=n;i++) 69 { 70 for (int j=head[i];j;j=e[j].next) 71 if (be[i]!=be[e[j].v]) 72 { 73 ++sum; 74 nw[sum].u=be[i]; 75 nw[sum].v=be[e[j].v]; 76 nw[sum].next=hd[be[i]]; 77 hd[be[i]]=sum; 78 ++enter[be[e[j].v]]; 79 } 80 } 81 for (int i=1;i<=n;i++) 82 { 83 if (!vis[be[i]]&&!enter[be[i]]) 84 { 85 vis[be[i]]=1; 86 res++; 87 } 88 } 89 for (int i=1;i<=n;i++) 90 { 91 if (num[be[i]]==1&&vis[be[i]]) 92 { 93 int flag=0; 94 for (int j=hd[be[i]];j;j=nw[j].next) 95 if (enter[nw[j].v]<=1) {flag=1;break;} 96 if (!flag) {res--;break;} 97 } 98 } 99 double ans=(double)(1.0-(double)(1.0*res/n)); 100 printf("%.6lf",ans); 101 return 0; 102 }