【NOIP2018复习】杀人游戏 (tarjan/强连通分量)

杀人游戏 

Time Limits: 1000 ms  Memory Limits: 512000 KB  Detailed Limits  

Description

  一位冷血的杀手潜入Na-wiat,并假装成平民。警察希望能在N个人里面,查出谁是杀手。
  警察能够对每一个人进行查证,假如查证的对象是平民,他会告诉警察,他认识的人,谁是杀手,谁是平民。假如查证的对象是杀手,杀手将会把警察干掉。
  现在警察掌握了每一个人认识谁。
  每一个人都有可能是杀手,可看作他们是杀手的概率是相同的。
  问:根据最优的情况,保证警察自身安全并知道谁是杀手的概率最大是多少?

Input

  输入文件killer.in,第一行有两个整数N,M。
  接下来有M行,每行两个整数x,y,表示x认识y(y不一定认识x,例如胡锦涛同志)。

Output

  输出文件killer.out仅包含一行一个实数,保留小数点后面6位,表示最大概率。

Sample Input

5 4
1 2
1 3
1 4
1 5

Sample Output

0.800000

Data Constraint

Hint

【样例解释】
  警察只需要查证1。假如1是杀手,警察就会被杀。假如1不是杀手,他会告诉警察2,3,4,5谁是杀手。而1是杀手的概率是0.2,所以能知道谁是杀手但没被杀的概率是0.8。
【数据规模】
  对于30%的数据有1≤N ≤ 10,0≤M ≤10
  对于100%的数据有1≤N ≤ 10 0000,0≤M ≤ 30 0000.

题解:对于每一个点,期望被杀概率为1/n,答案为1-num/n,num为选取的点数

            要使选取的点数

const
  maxn=100000;
  maxm=300000;
  inf='investigation10.in';

var
  last,next,last1,next1:array[0..maxm]of longint;
  e,e1:array[0..maxm,1..2]of longint;
  n,m,scc,sum,sum1:longint;
  v,ju:array[1..maxn]of boolean;
  dfn,low,belong:array[1..maxn]of longint;
  du,stack:array[0..maxn]of longint;
  i,j,flag,ans,cou,pp,top:longint;


procedure add(x,y:longint);
begin
  inc(sum);
  e[sum,1]:=x;e[sum,2]:=y;
  next[sum]:=last[x];
  last[x]:=sum;
end;

procedure add1(x,y:longint);
begin
  inc(sum1);
  e1[sum1,1]:=x;e1[sum1,2]:=y;
  next1[sum1]:=last[x];
  last1[x]:=sum1;
end;

procedure init;
var
  i,x,y:longint;
begin
  readln(n,m);
  for i:=1 to m do
  begin
    readln(x,y);
    add(x,y);
  end;
end;

function min(a,b:longint):longint;
begin
  if a<b then exit(a) else exit(b);
end;

procedure tarjan(x:longint);
var
  i,go:longint;
begin
  inc(cou);
  dfn[x]:=cou;low[x]:=cou;

  v[x]:=true;
  inc(top);
  stack[top]:=x;
  i:=last[x];
  while i<>0 do
  begin
    go:=e[i,2];
    if dfn[go]=0 then
    begin
      tarjan(go);
      low[x]:=min(low[x],low[go]);
    end
    else
    if v[go] then
    begin
      low[x]:=min(low[x],dfn[go]);
    end;
    i:=next[i];
  end;
  if dfn[x]=low[x] then
  begin
    v[x]:=false;
    inc(scc);
    belong[x]:=scc;
    if stack[top]=x then ju[scc]:=true;
    while stack[top]<>x do
    begin
      belong[stack[top]]:=scc;
      v[stack[top]]:=false;
      dec(top);
    end;
    dec(top);
  end;
end;

procedure build;
var
  i,kk:longint;
begin
  for i:=1 to m do
  begin
    if belong[e[i,1]]<>belong[e[i,2]] then
    begin
      inc(du[belong[e[i,2]]]);
      add1(belong[e[i,1]],belong[e[i,2]]);
    end;
  end;
end;

begin
 // assign(input,inf);reset(input);
  //assign(output,ouf);rewrite(Output);
  init;
  for i:=1 to n do
  begin
    if dfn[i]<>0 then continue;
    cou:=1;
    tarjan(i);
  end;
  build;
  for i:=1 to scc do
    if du[i]=0 then
    begin
      inc(ans);
      if flag=1 then continue;
      if ju[i] then
      begin
        j:=last[i];
        flag:=1;
        while j<>0 do
        begin
          if du[e1[j,2]]<=1 then
          begin
            flag:=0;
            break;
          end;
          j:=next[j];
        end;
      end;
    end;
  writeln((1-(ans-flag)/n):0:6);
  //close(input);
end.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值