概率期望训练之三

http://acm.hdu.edu.cn/showproblem.php?pid=5036

题目大意:

开始你没有任何钥匙,而你面前有很多道门,每道门后面可能有打开其他门的钥匙,如果你无法打开这道门,可以选择将它炸开,求打开所有门的期望炸的次数

分析:

首先根据期望的线性性

炸开所有门的期望次数=Σ每道门的期望次数

但这道题烦就烦在有可能会有环

先建图

假如没有环的话,ans=Σ1/dep[i]

但现在有环怎么办?

你肯定和我想的一样,Tarjan缩点呗

然后就莫名其妙的wa了(应该是我写错了,正解只是更简化了)

wa code:(不想看就跳过)

#include<bits/stdc++.h>
#define ll long long
#define il inline
#define ri register int
#define lowbit(x) x&(-x)
using namespace std;
const int maxn=1e3+5;
bool vis[maxn];
queue<int>S;
vector<int>Q[maxn],t[maxn];
double ans;
int T,cnt,top,sum,n,ca;
int dfn[maxn],deg[maxn],sz[maxn],belong[maxn],low[maxn],stk[maxn],dep[maxn];
il void Tarjan(int u){
	dfn[u]=low[u]=++cnt;vis[u]=true;
	stk[++top]=u;
	for(ri i=0;i<Q[u].size();i++){
		int to=Q[u][i];
		if(!dfn[to])Tarjan(to),low[u]=min(low[to],low[u]);
		else if(vis[u])low[u]=min(dfn[to],low[u]);
	}
	if(dfn[u]==low[u]){
		++sum;sz[sum]=0;t[sum].clear();int tp;
		do{
			tp=stk[top--];
			vis[tp]=false;
			belong[tp]=sum;
			sz[sum]++;
		}while(tp!=u);
}}
int main(){
	scanf("%d",&T);
	while(T--){
		scanf("%d",&n);
		for(ri i=1;i<=n;i++){deg[i]=dfn[i]=low[i]=dep[i]=0;
			int tot;scanf("%d",&tot);
			Q[i].clear();
			for(ri j=1;j<=tot;j++){
			int x;scanf("%d",&x);
			if(x==i)continue;
			Q[i].push_back(x);
			}
		}
		cnt=top=sum=ans=0;
		for(ri i=1;i<=n;i++)if(!dfn[i])Tarjan(i);
		for(ri u=1;u<=n;u++)
			for(ri i=0;i<Q[u].size();i++){
				int to=Q[u][i];
				if(belong[to]!=belong[u])t[belong[u]].push_back(belong[to]),deg[belong[to]]++;
			}
			for(ri i=1;i<=sum;i++)if(!deg[i]){S.push(i),dep[i]=sz[i];}
			while(!S.empty()){
				int tmp=S.front();S.pop();
				ans+=(double)sz[tmp]/dep[tmp];
			for(ri i=0;i<Q[tmp].size();i++){
				int to=Q[tmp][i];
				deg[to]--;dep[to]+=dep[tmp];
			    if(!deg[to]){
			    	dep[to]+=sz[to];
			    	S.push(to);
				}
			}	
			}
		printf("Case #%d: %.05f\n", ++ca, ans);
	}
	return 0;
}
/*
2
3
1 2
1 3
1 1
3
0
0
0
*/

正解

考虑传递闭包:

所谓传递闭包就是A能够到B,B能够到C,那么A也就能够到C

考虑集合S能够到A,集合T能够到B,A能够到B,则A|S|T就能够到B

bitset维护即可

#include <iostream>
#include <cstring>
#include <cstdio>
#include <bitset>
#include <algorithm>
using namespace std;
const int maxn = 1e3 + 5;
bitset<maxn> v[maxn];
int main()
{
    int t, ca = 1, n, k, x;
    cin >> t;
    while(t--)
    {
        scanf("%d", &n);
        for (int i = 0; i <= n; i++)
            v[i].reset(),v[i][i] = true;//它自己也要算上 
        for(int i = 1; i <= n; i++)
        {
            scanf("%d", &k);
            for(int j = 1; j <= k; j++)
                scanf("%d", &x),v[i][x] = 1;
        }
        for(int i = 1; i <= n; i++)  
            for(int j = 1; j <= n; j++)
                if(v[j][i])
                    v[j] |= v[i];//传递闭包 
        double ans = 0;
        for(int i = 1; i <= n; i++)
        {
            int cnt = 0;
            for(int j = 1; j <= n; j++)
                if(v[j][i]) cnt++;
            ans += 1.0 / cnt;
        }
        printf("Case #%d: %.05f\n", ca++, ans);
    }
    return 0;
}
posted @ 2019-10-27 20:54  wzx_believer  阅读(127)  评论(0编辑  收藏  举报