BZOJ 2438 [中山市选2011]杀人游戏 ——期望DP
发现每一次死亡的几率相等,所以只需要判断最少问多少人即可。
并且环上的点任意询问都可以。
所以直接Tarjan缩点,然后计算入度为0的点的数目。
但是还有一些情况的时候会减少一次询问,比如说:$1->3,2->3$此时只需要询问1或2即可,因为必然有一个人是杀手。
所以需要一系列特殊判断,但是自己写挂了,天真的以为在$n=1$的时候几率为0,然后挂掉了,显然并不需要询问。
#include <map> #include <cmath> #include <queue> #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; #define F(i,j,k) for (int i=j;i<=k;++i) #define D(i,j,k) for (int i=j;i>=k;--i) #define ll long long #define mp make_pair #define maxn 600005 struct Graph{ int n,fr[maxn],h[maxn],to[maxn],ne[maxn],en,inside[maxn]; int sta[maxn],top,bel[maxn],idx,vcnt,siz[maxn]; int dfn[maxn],low[maxn],ins[maxn],in[maxn],out[maxn]; void init(){memset(h,-1,sizeof h); en=0;idx=0;vcnt=0;} void add(int a,int b) {fr[en]=a;to[en]=b;ne[en]=h[a];h[a]=en++;out[a]++;in[b]++;} void Tarjan(int o) { low[o]=dfn[o]=++idx;ins[o]=1; sta[++top]=o; for (int i=h[o];i>=0;i=ne[i]) if (!low[to[i]]) Tarjan(to[i]),low[o]=min(low[o],low[to[i]]); else if (ins[to[i]]) low[o]=min(low[o],dfn[to[i]]); if (low[o]==dfn[o]) { int x=-1; vcnt++; while (x!=o) { x=sta[top--]; bel[x]=vcnt; siz[vcnt]++; ins[x]=0; } } } void Solve(){F(i,1,n)if(!dfn[i])Tarjan(i);} int cal() { int ret=0; F(i,1,n) if (!in[i]) ret++; return ret; } int flag() { // memset(vis,0,sizeof vis); int ret=0; F(i,1,n) { if (siz[bel[i]]==1&&in[i]==0) { int tmp=0x3f3f3f3f,flag=0; for (int j=h[i];j>=0;j=ne[j]) { flag=1; tmp=min(inside[bel[to[j]]],tmp); } if ((tmp>=2&&flag)||in[i]+out[i]==0) ret=1; } } return ret; } }G1,G2; int n,m; int vis[maxn]; int main() { // freopen("killer1.in","r",stdin); scanf("%d%d",&n,&m);G1.init();G2.init(); // if (n==1){printf("0.000000\n");return 0;} F(i,1,m){int u,v;scanf("%d%d",&u,&v);G1.add(u,v);} G1.n=n;G1.Solve(); F(i,1,n) for (int j=G1.h[i];j>=0;j=G1.ne[j]){ if (G1.bel[G1.fr[j]]!=G1.bel[G1.to[j]]&&vis[G1.to[j]]!=i) { vis[G1.to[j]]=i; G2.add(G1.bel[G1.fr[j]],G1.bel[G1.to[j]]); G1.inside[G1.bel[G1.to[j]]]++; } } G2.n=G1.vcnt; printf("%.6f\n",1.0*(n-G2.cal()+G1.flag())/(n*1.0)); }