您的位置:首页 > 其它

【BZOJ 3925】【ZJOI 2015】[概率dp]地震后的幻想乡

2017-03-14 12:37 483 查看

题目描述

BZOJ3925

题目分析

PoPoQQQ大爷的概率DP看不懂,看了另外一个大神的题解…好像跟概率dp没什么关系。

根据提示,对于n个[0,1]之间的随机变量x1,x2,...,xm,第k小的那个的期望值是km+1,那么,我们不妨计算出在整个图中刚好选择k条边使得该图联通的概率,乘以它的贡献km+1,因为若算出选择k条边而使其联通的概率并不能保证其刚好是k条边,于是,我们不妨处理出Ps,i表示对于点集s,选择i条边而不连通的概率,但是我们要求的是选择i条边刚好连通的概率,很明显就是(1−Ps,i)−(1−Ps,i−1),所以Ans=1m+1×(Pall,0−Pall,1)+2m+1×(Pall,1−Pall,2)+...+m+1m+1×(Pall,m)

把P提出来就有Ans=Σmi=0Pall,im+1。

现在的任务是怎么计算P,我们可以先把不连通的方案数算出来再除以总方案数,设不连通的方案数为fs,i,连通的方案数为gs,i。就有fs,i+gs,i=Ccnts,i(cnts为s形成的子图中的边数)

对于计算fs,我们不妨随便找一个定点,而枚举定点所在s的真子集,在累加一下方案数就可以求出fs,fs,i+j=gt,i∗Ccnts−t,j| i∈[ 0 , cnts] , j∈[ 0 , cnts−t],因为没有枚举s和s−t中的边,所以说s和s−t必然不连通。

具体细节可以参考代码。

代码

/**************************************************************
Problem: 3925
User: bzjudge2
Language: C++
Result: Accepted
Time:104 ms
Memory:2384 kb
****************************************************************/

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;

#define MAXN 10
#define MAXM 50
#define MAXS 1024
#define INF 0x3f3f3f3f
typedef long long int LL;

template<class T>
void Read(T &x){
x=0;char c=getchar();bool flag=0;
while(c<'0'||'9'<c){if(flag)x=-x;c=getchar();}
while('0'<=c&&c<='9'){x=x*10+c-'0';c=getchar();}
if(flag)x=-x;
}

int n,m;
int to[MAXN+10];
int cnt[MAXS+100],sz[MAXS+100];
LL f[MAXS+100][MAXM+10];//S中选j条边使得不连通
LL g[MAXS+100][MAXM+10];//S中选j条边使得连通
LL C[MAXM+10][MAXM+10];

void init(){
memset(g,0,sizeof(g));
memset(f,0,sizeof(f));

C[0][0]=1;
for(int i=1;i<=MAXM;++i){
C[i][0]=C[i][i]=1;
for(int j=1;j<i;++j)
C[i][j]=C[i-1][j-1]+C[i-1][j];
}
}

int main(){
init();

Read(n),Read(m);
int a,b;
for(int i=1;i<=m;++i){
Read(a),Read(b);
--a,--b;
to[b]|=1<<a;
to[a]|=1<<b;
}

int ed=1<<n;
for(int s=1;s<ed;++s){
sz[s]=sz[s>>1]+(s&1);
if(sz[s]==1){
g[s][0]=1;
continue;
}

for(int i=0;i<n;++i)
if(s&(1<<i))cnt[s]+=sz[to[i]&s];//i点可以到达的s内点的边数
cnt[s]>>=1;

int lowbit=s&-s;//任选一点
for(int t=s&(s-1);t>0;t=(t-1)&s)//枚举每一中包含lowbit的s子集
if(t&lowbit){
for(int i=0;i<=cnt[t];++i)//t中选择的边数
for(int j=0;j<=cnt[s^t];++j)//s-t中选择的边数
f[s][i+j]+=g[t][i]*C[cnt[s^t]][j];//保证不选择s~t的边
}

for(int i=0;i<=cnt[s];++i)
g[s][i]=C[cnt[s]][i]-f[s][i];
}

double ans=0;
for(int i=0;i<=m;++i)
ans+=(double)f[ed-1][i]/C[cnt[ed-1]][i];
printf("%0.6lf\n",ans/(m+1));
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息