luogu P3343 [ZJOI2015]地震后的幻想乡
题面传送门
首先根据题目中的提示,我们可以直接将最小的边设为\(\frac{1}{m+1}\),第\(k\)小的边设为\(\frac{k}{m+1}\),于是题面中的期望就没了。
于是我们要求出恰好前\(k\)条边联通的概率\(p_k\),则答案为\(\sum\limits_{i=1}{m}{p_i\times \frac{i}{m+1}}\)。
恰好不太好做,考虑差分,我们求出\(f_i\)表示加入前\(i\)条边不连通的概率,则答案为\(\sum\limits_{i=0}^{m}{\frac{f_i}{m+1}}\)
数据范围这么小考虑状压dp,设\(f_{i,S}\)表示\(S\)集合中选取了\(i\)条边不连通的概率,参照P4841 [集训队作业2013]城市规划的方法,我们枚举\(S\)中包含最小号节点的联通块是啥,然后枚举这个联通块中选了几条边,组合数算算贡献即可。
时间复杂度\(O(3^nm^2)\)但是跑得飞快。
code:
#include<bits/stdc++.h>
#define I inline
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define abs(x) ((x)>0?(x):-(x))
#define ll long long
#define db double
#define lb long db
#define N (10+5)
#define M ((1<<10)+5)
#define K (131071)
#define mod 998244353
#define Mod (mod-1)
#define eps (1e-9)
#define ull unsigned ll
#define it iterator
#define Gc() getchar()
#define Me(x,y) memset(x,y,sizeof(x))
#define Mc(x,y) memcpy(x,y,sizeof(x))
#define d(x,y) ((k+1)*(x)+(y))
#define R(n) (1ll*rand()*rand()%(n)+1)
#define Pc(x) putchar(x)
#define LB lower_bound
#define UB upper_bound
#define PB push_back
using namespace std;
int n,m,k,x,X[N*N],Y[N*N],G[M];db dp[N*N][M],C[N*N][N*N],Ans;
int main(){
// freopen("1.in","r",stdin);
int i,j,x,y,z;scanf("%d%d",&n,&m);k=(1<<n)-1;for(i=1;i<=m;i++) scanf("%d%d",&X[i],&Y[i]),X[i]--,Y[i]--;
for(i=0;i<=m;i++) for(C[i][0]=j=1;j<=i;j++) C[i][j]=C[i-1][j]+C[i-1][j-1];
for(i=0;i<=k;i++) for(j=1;j<=m;j++) i>>X[j]&1&&i>>Y[j]&1&&(G[i]++);for(i=1;i<=k;i++){
for(j=0;j<n;j++) if(i>>j&1) {z=j;break;}for(j=(i-1)&i;j;j=(j-1)&i){
if(!(j>>z&1)) continue;for(x=0;x<=G[j];x++) for(y=0;y<=G[i^j];y++) dp[x+y][i]+=(1-dp[x][j])*C[G[j]][x]*C[G[i^j]][y]/C[G[i]][x+y];
}
}for(i=0;i<=m;i++)Ans+=dp[i][k]*1.0/(m+1);printf("%.6lf\n",Ans);
}