1. 程式人生 > >大包子玩遊戲

大包子玩遊戲

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);
    }
}

大包子玩遊戲