记录编号 227696 评测结果 AAAAAAAAAAAAAAAAAAAA
题目名称 [ZJOI 2015]地震后的幻想乡 最终得分 100
用户昵称 Gravatar神利·代目 是否通过 通过
代码语言 C++ 运行时间 0.061 s
提交时间 2016-02-18 19:55:51 内存使用 1.95 MiB
显示代码纯文本
#include<stdio.h>
int n,m,maxn,e[11],sz[1<<11],cnt[1<<11];
long long c[55][55],f[1<<11][55],g[1<<11][55];
double ans;
void DP(){
	for(int s=1;s<maxn;++s){
		sz[s]=sz[s>>1]+(s&1);//sz[s]代表s集合中点的数量
		if(sz[s]==1){//若s未单独一个点,显然方案数为1
			g[s][0]=1;
			continue;
		}
		for(int i=0;i<n;++i)
		    if((s>>i)&1)//更新当前s集合的边数
		        cnt[s]+=sz[e[i]&s];
		cnt[s]>>=1;//因为是双向边,所以除以2
		int lowbit=s&-s;
		for(int t=(s-1)&s;t;t=(t-1)&s)//枚举s的真 ・子集
		    if(t&lowbit)//从s里取一定点,这样方案数就不重不漏了!
		        for(int i=0;i<=cnt[t];++i)
		            for(int j=0;j<=cnt[s^t];++j)
		                f[s][i+j]+=g[t][i]*c[cnt[s^t]][j];
		for(int i=0;i<=cnt[s];++i)
		    g[s][i]=c[cnt[s]][i]-f[s][i];
	}
}
int main(){
	freopen("zjoi15_mst.in","r",stdin);
	freopen("zjoi15_mst.out","w",stdout);
	int u,v;scanf("%d%d",&n,&m);maxn=1<<n;
	for(int i=0;i<m;++i) scanf("%d%d",&u,&v),--u,--v,e[u]|=1<<v,e[v]|=1<<u;
	c[0][0]=1;
	for(int i=1;i<=m;++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];
	}
	DP();
	for(int i=0;i<=m;++i) ans+=(double)f[maxn-1][i]/c[cnt[maxn-1]][i];
	ans/=m+1;//答案的计算方式很特别,是一个逐步累加的过程!
	printf("%.6lf",ans);
	//while(1);
}