码迷,mamicode.com
首页 > 其他好文 > 详细

大包子玩游戏

时间:2018-10-05 17:22:09      阅读:200      评论:0      收藏:0      [点我收藏+]

标签:情况   strong   拓扑排序   alc   count   namespace   其他   lin   cal   

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

大包子玩游戏

标签:情况   strong   拓扑排序   alc   count   namespace   其他   lin   cal   

原文地址:https://www.cnblogs.com/yoyoball/p/9745072.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!