大包子玩游戏 - 结论题 - 强连通分量 - 拓扑排序

题目大意:给你一张有向图,每次随机从剩下的点中选择一个,删掉这个点以及其能够到达的点。问期望几次能把整张图删光。 n ≤ 1000 n\le1000 n1000
题解:
根据期望的线性性,考虑每个点对答案有1的贡献的概率,即其有多大的概率会被选择,显然等价于能够删掉他的点都在他之后被删掉,那么概率显然是 1 s \frac{1}{s} s1,其中s为能够到达它的点的数量。这样tarjan缩点后跑一个bitset即可。

 
#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define Rep(i,v) rep(i,0,(int)v.size()-1)
#define lint long long
#define ull unsigned lint
#define db long double
#define pb push_back
#define mp make_pair
#define fir first
#define sec second
#define gc getchar()
#define N 1010
#define debug(x) cerr<<#x<<"="<<x
#define sp <<" "
#define ln <<endl
using namespace std;
typedef pair<int,int> pii;
typedef set<int>::iterator sit;
inline int inn()
{
    int x,ch;while((ch=gc)<'0'||ch>'9');
    x=ch^'0';while((ch=gc)>='0'&&ch<='9')
        x=(x<<1)+(x<<3)+(ch^'0');return x;
}
#define clr(a,n) memset(a,0,sizeof(int)*((n)+1))
int vis[N],bel[N],sz[N],cnt,dfc,low[N],dfn[N],lst[N],d[N],clc[N][N];
stack<int> s;bitset<N> to[N];vector<int> g[N],t[N];queue<int> q;
 
int tarjan(int x)
{
    vis[x]=1,low[x]=dfn[x]=++dfc,s.push(x);int y;
    Rep(i,g[x])
        if(!vis[y=g[x][i]]) low[x]=min(low[x],tarjan(y));
        else if(vis[y]==1) low[x]=min(low[x],dfn[y]);
    if(low[x]==dfn[x])
    {
        to[++cnt].reset(),bel[x]=cnt,vis[x]=2,sz[cnt]=1,to[cnt].set(x);
        for(;s.top()!=x;s.pop())
            vis[s.top()]=2,sz[cnt]++,bel[s.top()]=cnt,to[cnt].set(s.top());
        s.pop();
    }
    return low[x];
}
int main()
{
    for(int Tcs=inn(),curTcs=1;curTcs<=Tcs;curTcs++)
    {
        int n=inn();db ans=0;rep(i,1,n) g[i].clear();
        rep(i,1,n) for(int k=inn();k;k--) g[i].pb(inn());
         
        dfc=0,clr(vis,n),cnt=0;
        while(!s.empty()) s.pop();
        rep(i,1,n) if(!vis[i]) tarjan(i);
         
        rep(i,1,cnt) t[i].clear(),d[i]=0;
        rep(i,1,n) Rep(j,g[i])
            if(g[i][j]&&bel[i]!=bel[g[i][j]])
                t[bel[g[i][j]]].pb(bel[i]),d[bel[i]]++;
         
        while(!q.empty()) q.pop();int c=0;
        rep(i,1,cnt) if(!d[i]) q.push(i);
        while(!q.empty())
        {
            int x=q.front(),y;q.pop(),lst[++c]=x;
            Rep(i,t[x]) if(!(--d[y=t[x][i]])) q.push(y);
        }
         
        rep(i,1,cnt) clr(clc[i],cnt);
        for(int k=c;k>=1;k--)
        {
            int x=lst[k];
            Rep(i,t[x]) if(!clc[x][t[x][i]]) to[x]|=to[t[x][i]],clc[x][t[x][i]]=1;
        }
         
        rep(i,1,cnt) ans+=(db)sz[i]/to[i].count();
         
        printf("Case #%d: %.5lf\n",curTcs,(double)ans);
    }
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值