1. 程式人生 > >[BZOJ 2438] 中山市選2011 殺人遊戲

[BZOJ 2438] 中山市選2011 殺人遊戲

next include IT top graph pair back -c scanf

[BZOJ 2438] 中山市選2011 殺人遊戲

<題目鏈接>


AZe 問的題目,上手發現確實好題 qwq。

如何求出題目要求的概率?

根據題目描述,我們知道,警察調查未知身份者就有可能會死亡,所以查的人越少越安全。

因此,安全查出必須調查的未知身份者數總人數 \(P_{安全查出} = 1 -\frac{必須調查的未知身份者數}{總人數}\)

如果有一群人,他們直接或間接認識,那麽他們可以看作一個點。

於是想到 Tarjan 求 SCC,縮點建新圖(原因過後交代)。

查一個點,這個點認識的點,都可以明確身份。

所以,查所有新圖上入度為 \(0\) 的點即可,數出個數 \(\mathrm{ans}\)

特殊情況如下:如果有一個人 \(x\) 被孤立,即,\(x\) 所在的 SCC \(\mathrm{size}\)\(1\),且 \(x\) 認識的每個人都不僅僅被 \(x\) 認識(判斷這步需要在新圖上遍歷 \(x\) 所在點的鄰接點,所以需要建新圖),這時候不需要詢問 \(x\) 便可以確定其身份。這時候需 --\(\mathrm{ans}\)

答案即 \(1 - \frac{ans}{n}\)

#include <algorithm>
#include <cstdio>
#include <set>
#include <stack>
const int MAXN=100010; int n,m; class Graph { private: int V; public: Graph(int n) { V=n; for(int i=1;i<=V;++i) head[i]=nullptr; } ~Graph(void) { for(int i=1;i<=V;++i) delete
head[i]; } struct Edge { int to; Edge *next; Edge(int to,Edge* next):to(to),next(next){} ~Edge(void) { if(next!=nullptr) delete next; } }*head[MAXN]; void AddEdge(int u,int v) { head[u]=new Edge(v,head[u]); } }*G1,*G2; namespace Tarjan { bool exist[MAXN]; int cnt,sum,ans,DFN[MAXN],low[MAXN],SCC[MAXN],size[MAXN],in[MAXN]; std::set<std::pair<int,int> > S; std::stack<int> st; void DFS(int u) { st.push(u); exist[u]=true; DFN[u]=low[u]=++cnt; for(Graph::Edge *i=G1->head[u];i!=nullptr;i=i->next) { int v=i->to; if(!DFN[v]) { DFS(v); low[u]=std::min(low[u],low[v]); } else if(exist[v]) low[u]=std::min(low[u],DFN[v]); } if(DFN[u]==low[u]) { ++sum; for(int v=0;u^v;) { exist[v=st.top()]=false; st.pop(); ++size[SCC[v]=sum]; } } } void Contract(void) { for(int u=1;u<=n;++u) for(Graph::Edge *i=G1->head[u];i!=nullptr;i=i->next) { int v=i->to; if(SCC[u]^SCC[v] && !S.count(std::make_pair(SCC[u],SCC[v]))) { ++in[SCC[v]]; G2->AddEdge(SCC[u],SCC[v]); S.insert(std::make_pair(SCC[u],SCC[v])); } } } bool Judge(int u) { if(in[u] || size[u]!=1) return false; for(Graph::Edge *i=G2->head[u];i!=nullptr;i=i->next) if(in[i->to]==1) return false; return true; } void Run(void) { for(int i=1;i<=n;++i) if(!DFN[i]) DFS(i); G2=new Graph(sum); Contract(); for(int i=1;i<=sum;++i) if(!in[i]) ++ans; for(int i=1;i<=sum;++i) if(Judge(i)) { --ans; break; } printf("%.6lf\n",(double)(n-ans)/n); } } int main(int argc,char** argv) { scanf("%d %d",&n,&m); G1=new Graph(n); for(int i=1,x,y;i<=m;++i) { scanf("%d %d",&x,&y); G1->AddEdge(x,y); } Tarjan::Run(); return 0; }

謝謝閱讀。

[BZOJ 2438] 中山市選2011 殺人遊戲