您的位置:首页 > 其它

bzoj2438 杀人游戏 Tarjan强联通分量

2016-02-23 18:15 369 查看
显然知道谁是杀手相当于知道所有人的身份。因此题目的答案即在无向图中选择最少的点,使得能遍历到至少n-1个点(最后一个点可以推理得到)。设结果为x,则答案为(n-x)/n。

所以就可以用Tarjan找出强联通分量然后缩点,得到的DAG上入度为0的点即所要选择的点。如果存在某个点,这个点所在的强联通分量大小为1而且这个店所有的出边到达的点的入度都>1,那么这个点不选也可以遍历到n-1个点,x可以减一。

AC代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
#define N 100005
#define M 600005
using namespace std;

int n,m,tot,cnt,tp,dfsclk,fst
,pnt[M],nxt[M],pos
,low
,stk
,scc
,sz
;
int et
,u[M],v[M]; bool vis
;
int read(){
int x=0; char ch=getchar();
while (ch<'0' || ch>'9') ch=getchar();
while (ch>='0' && ch<='9'){ x=x*10+ch-'0'; ch=getchar(); }
return x;
}
void add(int aa,int bb){
pnt[++tot]=bb; nxt[tot]=fst[aa]; fst[aa]=tot;
}
void dfs(int x){
pos[x]=low[x]=++dfsclk; stk[++tp]=x; int p;
for (p=fst[x]; p; p=nxt[p]){
int y=pnt[p];
if (!pos[y]){ dfs(y); low[x]=min(low[x],low[y]); }
else if (!scc[y]) low[x]=min(low[x],pos[y]);
}
if (low[x]==pos[x])
for (cnt++; ; tp--){
sz[cnt]++; scc[stk[tp]]=cnt;
if (stk[tp]==x){ tp--; break; }
}
}
bool jdg(int x){
int p;
for (p=fst[x]; p; p=nxt[p]) if (et[pnt[p]]==1) return 0;
return 1;
}
int main(){
n=read(); m=read(); int i,j,p,ans=0;
if (n==1){ puts("1.000000"); return 0; }
for (i=1; i<=m; i++){
int x=read(),y=read(); add(x,y);
}
for (i=1; i<=n; i++) if (!pos[i]) dfs(i);
m=0;
for (i=1; i<=n; i++){
for (p=fst[i]; p; p=nxt[p]){
j=scc[pnt[p]];
if (!vis[j] && scc[i]!=j){
vis[j]=1;
u[++m]=scc[i]; v[m]=j;
}
}
for (p=fst[i]; p; p=nxt[p]) vis[scc[pnt[p]]]=0;
}
tot=0; for (i=1; i<=cnt; i++) fst[i]=0;
for (i=1; i<=m; i++){ add(u[i],v[i]); et[v[i]]++; }
for (i=1; i<=cnt; i++) if (!et[i]) ans++;
for (i=1; i<=cnt; i++)
if (!et[i] && sz[i]==1 && jdg(i)){
ans--; break;
}
printf("%.6f\n",(double)(n-ans)/n);
return 0;
}


by lych
2016.2.23
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: