zoukankan      html  css  js  c++  java
  • 大包子玩游戏

    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("
    ");
    		printf("Case #%d: %.5lf
    ",o,ans);
    	}
    }
    
  • 相关阅读:
    并发编程
    进程的介绍
    操作系统详解
    进程的粗略理解
    打印进度条
    FTP上传下载文件(面向对象版)
    socket套接字
    FTP上传下载文件(函数简易版)
    osi七层协议 Open System Interconnection
    __str__和__repr__的区别
  • 原文地址:https://www.cnblogs.com/yoyoball/p/9745072.html
Copyright © 2011-2022 走看看