BZOJ 2438: [中山市选2011]杀人游戏
Description
给你一个有向图,求至少询问多少次能够得到全部点的信息.
Sol
Tarjan + 强连通分量缩点 + 判断.
先缩点,如果我们知道了强连通分量里的任意一个点,我们就可以知道这些点的信息,和这些点所连接的其他点的信息,显然我们需要知道的就是缩点后入度为0的点.
然后还有最后一个入度为0的单点,并且他的所有儿子都能被其他节点访问,那么最后就可以直接推断出这个点,所以要删去.
Code
#include<cstdio> #include<stack> #include<vector> #include<iostream> using namespace std; const int N = 100005; #define debug(a) cout<<#a<<"="<<a<<" " #define ct cout<<endl #define _ct cout<<"----------"<<endl int n,m,cnt,bcnt; int dfsn[N],low[N],ins[N],b[N],sz[N],vis[N]; vector<int> g[N]; vector<int> h[N]; int du[N]; stack<int> stk; inline int in(int x=0,char ch=getchar()){ while(ch>'9'||ch<'0') ch=getchar(); while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();return x; } void Tarjan(int u){ dfsn[u]=low[u]=++cnt,ins[u]=1,stk.push(u); for(int i=0,lim=g[u].size(),v;i<lim;i++){ v=g[u][i]; if(!dfsn[v]){ Tarjan(v),low[u]=min(low[u],low[v]); } else if(ins[v]) low[u]=min(low[u],dfsn[v]); }if(dfsn[u]==low[u]){ ++bcnt;int v=-1; for(;u!=v;){ v=stk.top(),stk.pop(),ins[v]=0,b[v]=bcnt,sz[bcnt]++; } } } int check(int x){ for(int i=0,lim=h[x].size();i<lim;i++) if(du[h[x][i]]<=1) return 0; return 1; } int main(){ // freopen("in.in","r",stdin); n=in(),m=in(); for(int i=1,u,v;i<=m;i++) u=in(),v=in(),g[u].push_back(v); for(int i=1;i<=n;i++) if(!dfsn[i]) Tarjan(i); // _ct;debug(bcnt),ct; for(int i=1;i<=n;i++){ for(int j=0,lim=g[i].size();j<lim;j++) if(b[i]!=b[g[i][j]]&&!vis[b[g[i][j]]]) vis[b[g[i][j]]]=1,h[b[i]].push_back(b[g[i][j]]),du[b[g[i][j]]]++; for(int j=0,lim=g[i].size();j<lim;j++) if(b[i]!=b[g[i][j]]) vis[b[g[i][j]]]=0; } // _ct; int ans=0; for(int i=1;i<=bcnt;i++) if(du[i]==0) ans++; for(int i=1;i<=bcnt;i++) if(du[i]==0&&sz[i]==1&&check(i)){ ans--;break; } // debug(ans),ct; printf("%.6lf\n",(double)(n-ans)/n); return 0; }