这道题运用了很多数学知识(唉,真羡慕你们高数好的…….)
首先我们扔掉提示。。。。。
尝试枚举最小生成树的最大边x,令
g(x)
表示答案<=x的概率
发现答案可以表示成
∫10xg′(x)dx
根据分部积分法,原式=
1−∫10g(x)dx=∫10(1−g(x))dx
即答案=答案>=x的概率的积分
(离散上也有类似公式,可用有限微积分导出,图形也很好想)
令
f(x)=1−g(x)
则对于f(x)问题转变成一张图,边有x的概率出现,图不联通的概率;
然后可以状压dp求解(注意到dp数组是关于x的多项式)
#include<bits/stdc++.h>
#define rep(i,k,n) for(int i=k;i<=n;i++)
#define PB push_back
using namespace std;
const int N=13;
typedef long long ll;
typedef __float128 ld;
typedef vector<ll> ploy;
ploy operator + (const ploy& a,const ploy& b){
ploy c(max(a.size(),b.size()),0);
int sz_a=a.size()-1,sz_b=b.size()-1;
rep(i,0,sz_a)c[i]+=a[i];
rep(i,0,sz_b)c[i]+=b[i];
return c;
}
ploy operator - (const ploy& a,const ploy& b){
ploy c(max(a.size(),b.size()),0);
int sz_a=a.size()-1,sz_b=b.size()-1;
rep(i,0,sz_a)c[i]+=a[i];
rep(i,0,sz_b)c[i]-=b[i];
return c;
}
ploy operator * (const ploy& a,const ploy& b){
ploy c(a.size()+b.size(),0);
int sz_a=a.size()-1,sz_b=b.size()-1;
rep(i,0,sz_a)rep(j,0,sz_b)c[i+j]+=a[i]*b[j];
return c;
}
ld jifen_1(const ploy& a){
ld res=0.0;
int sz_a=a.size()-1;
rep(i,0,sz_a)res+=(ld)a[i]/(i+1);
return res;
}
ploy f[1<<N],pow_1_x[N*N],g(1,1);
int a[N],n,m,cnt[1<<N],S;
void init(){
rep(i,1,S)cnt[i]=cnt[i-(i&-i)]+1;
pow_1_x[0].PB(1);
pow_1_x[1].PB(1);pow_1_x[1].PB(-1);
rep(i,2,m)
pow_1_x[i]=pow_1_x[i-1] * pow_1_x[1];
}
int main(){
scanf("%d%d",&n,&m);S=(1<<n)-1;
int x,y;
rep(i,1,m){
scanf("%d%d",&x,&y);
a[x]|=(1<<(y-1));
a[y]|=(1<<(x-1));
}init();
rep(s,1,S){
new (&f[s])ploy(1,1);
int who=s&-s;
for(int j=s^who;j;j=(j-1)&(s^who)){
int i=s^j,edge=0;
rep(k,1,n)if((i>>(k-1))&1)edge+=cnt[a[k]&j];
f[s]=f[s] - f[i]*pow_1_x[edge];
}
}g=g - f[S];
double ans=jifen_1(g);
printf("%.6lf",ans);
}
如果不扔掉提示,有更方便做法;
传送门
其实提示用上述方法也易证