大包子玩遊戲
阿新 • • 發佈:2018-10-05
tro sizeof %d 能夠 scrip uil font namespace ble
Portal -->qwq(貌似是。。2014 ACM/ICPC Asia Regional Beijing Online - E)
Description
有\(n\)個房間,每個房間裏面可能有一些其他房間的鑰匙,初始的時候所有的房門都是鎖上的,隨機炸門,問期望炸多少次才能打開所有的房間
數據範圍:\(n<=1000\)
Solution
日常期望算不出來==
比較套路的想法當然是建成一個有向圖,每個房間與這個房間內鑰匙對應的房間連一條有向邊,那麽問題就變成了。。在一個有向圖上面刪點(刪去某個點能夠到達的所有點),問期望刪多少次才能刪掉整張圖
整體不好考慮,我們直接對於每一個點考慮其貢獻
這裏稍微。。表述一下接下來說的“貢獻”的具體含義:不考慮期望的情況下,一個點對答案有\(1\)的貢獻,當且僅當將這個點刪除的那次操作,選的點就是它本身
對於一個點來說,我們記能夠達到這個點的點集為\(S\),那麽可以將這個點刪除的操作總共就有\(|S|\)種,但其中只有一種是選了自己的,所以一個點對答案貢獻的期望就是\(\frac{1}{|S|}\)
然後我們對所有的點的期望貢獻求個和就是答案了
? 現在的問題是怎麽算\(S\),那。。反正\(n=1000\)。。。那。。我們縮一下點變成一個DAG然後bitset記一下能夠到達這個每個scc的點有哪些,然後直接跑拓撲排序那樣轉移就好了
? 代碼大概長這個樣子
#include<iostream> #include<cstdio> #include<cstring> #include<queue> #include<bitset> using namespace std; const int N=1010; struct xxx{ int y,nxt; }a[N*N*2]; queue<int> q; int h[N],in[N],dfn[N],low[N],inst[N],st[N],h1[N]; int bl[N],sz[N]; bitset<N> ok[N]; double ans; int n,m,T,tot,dfn_t,top,cnt; void add(int x,int y,int *h){a[++tot].y=y; a[tot].nxt=h[x]; h[x]=tot;} void tarjan(int x){ int u; dfn[x]=low[x]=++dfn_t; inst[x]=1; st[++top]=x; for (int i=h[x];i!=-1;i=a[i].nxt){ u=a[i].y; if (!dfn[u]){ tarjan(u); low[x]=min(low[x],low[u]); } else if (inst[u]) low[x]=min(low[x],dfn[u]); } if (low[x]==dfn[x]){ ++cnt; sz[cnt]=0; ok[cnt].reset(); while (st[top]!=x){ bl[st[top]]=cnt; ok[cnt][st[top]]=1; inst[st[top--]]=0; ++sz[cnt]; } bl[x]=cnt; --top; ++sz[cnt]; inst[x]=0; ok[cnt][x]=1; } } void rebuild(){ cnt=0; dfn_t=0; top=0; for (int i=1;i<=n;++i) if (!dfn[i]) tarjan(i); memset(h1,-1,sizeof(h1)); for (int i=1;i<=n;++i){ for (int j=h[i];j!=-1;j=a[j].nxt){ if (bl[i]==bl[a[j].y]) continue; add(bl[i],bl[a[j].y],h1); ++in[bl[a[j].y]]; } } } void calc(){ int u,v; rebuild(); while (!q.empty()) q.pop(); for (int i=1;i<=n;++i) if (in[i]==0) q.push(i); while (!q.empty()){ v=q.front(); q.pop(); for (int i=h1[v];i!=-1;i=a[i].nxt){ u=a[i].y; ok[u]|=ok[v]; --in[u]; if (!in[u]) q.push(u); } } } void get_ans(){ int tmp; ans=0; for (int i=1;i<=cnt;++i){ tmp=ok[i].count(); if (!tmp) continue; ans+=(1.0*sz[i])/(1.0*tmp); } } void init(){ for (int i=1;i<=n;++i) h[i]=-1,in[i]=0,dfn[i]=0; tot=0; } int main(){ #ifndef ONLINE_JUDGE freopen("a.in","r",stdin); #endif int x,num; scanf("%d",&T); for (int o=1;o<=T;++o){ scanf("%d",&n); init(); for (int i=1;i<=n;++i){ scanf("%d",&num); for (int j=1;j<=num;++j){ scanf("%d",&x); add(i,x,h); } } calc(); get_ans(); //for (int i=1;i<=n;++i) printf("%d ",ok[i].count()); printf("\n"); printf("Case #%d: %.5lf\n",o,ans); } }
大包子玩遊戲