problem
luogu-P3343
solution
dp(i):dp(i):dp(i): 当恰好加入第 iii 小边时候,所有点联通的方案数。
则 ans=∑idpi(mi)im+1ans=\sum_i \frac{dp_i}{\binom mi}\frac{i}{m+1}ans=∑i(im)dpim+1i 。
重点是如何计算出 dp(i)dp(i)dp(i)。
这个恰好的限制不好搞。加第 iii 小的边之前不能提前联通,加后一定要联通。
转化一下,dp(i)=dp(i)=dp(i)= 加之前不连通的方案数 −-− 加之后不连通的方案数。
记 f(s,i):sf(s,i):sf(s,i):s 点集中的点之间连了 iii 条边后 sss 中的点仍然不连通的方案数。
记 g(s,i):sg(s,i):sg(s,i):s 点集中的点之间连了 iii 条边后 sss 中的点联通的方案数。
显然 f(s,i)+g(s,i)=(cnt(s)i)f(s,i)+g(s,i)=\binom{cnt(s)}{i}f(s,i)+g(s,i)=(icnt(s)),其中 cnt(s):cnt(s):cnt(s): 点集 sss 中的点之间的边数。
转移枚举 sss 的子集 ttt,满足 s&(−s)s\&(-s)s&(−s),即 sss 中最小元素点也在 ttt 集合内。
f(s,i)=∑(s&−s)∈t⊂s∑j=0ig(t,j)(s⊕ti−j)f(s,i)=\sum_{(s\&-s)\in t\subset s}\sum_{j=0}^ig(t,j)\binom{s\oplus t}{i-j} f(s,i)=(s&−s)∈t⊂s∑j=0∑ig(t,j)(i−js⊕t)
这种转移相当于以 sss 中最小元素点为参考,其所在的联通块为 ttt,其余看作不连通,且 (s⊕ti−j)\binom{s\oplus t}{i-j}(i−js⊕t) 任意选的边连出来的集合一定不与 ttt 有边相连。这样就不会算重了。
最后 dpi=f({1,2,...,n},i−1)−f({1,2,...,n},i)dp_i=f(\{1,2,...,n\},i-1)-f(\{1,2,...,n\},i)dpi=f({1,2,...,n},i−1)−f({1,2,...,n},i)。
在代码实现中统计答案略有不同,我是将这个式子稍微展开了一下。
ans=∑i=1mim+1f({1,2,...,n},i−1)−f({1,2,...,n},i)(mi)ans=\sum_{i=1}^m\frac{i}{m+1}\frac{f(\{1,2,...,n\},i-1)-f(\{1,2,...,n\},i)}{\binom mi} ans=i=1∑mm+1i(im)f({1,2,...,n},i−1)−f({1,2,...,n},i)
考虑相邻两位:
im+1f({1,2,...,n},i−1)−f({1,2,...,n},i)(mi)i+1m+1f({1,2,...,n},i)−f({1,2,...,n},i+1)(mi+1)\frac{i}{m+1}\frac{f(\{1,2,...,n\},i-1)-f(\{1,2,...,n\},i)}{\binom mi}\\ \frac{i+1}{m+1}\frac{f(\{1,2,...,n\},i)-f(\{1,2,...,n\},i+1)}{\binom m{i+1}} m+1i(im)f({1,2,...,n},i−1)−f({1,2,...,n},i)m+1i+1(i+1m)f({1,2,...,n},i)−f({1,2,...,n},i+1)
将 1m+1\frac{1}{m+1}m+11 提出来,发现 i∗−f({1,2,...,n},i)i*-f(\{1,2,...,n\},i)i∗−f({1,2,...,n},i) 且 (i+1)∗+f({1,2,...,n},i)(i+1)*+f(\{1,2,...,n\},i)(i+1)∗+f({1,2,...,n},i),相加其实就是加入了一次 f({1,2,...,n},i)f(\{1,2,...,n\},i)f({1,2,...,n},i)。
而 f({1,2,...,n},m)=0f(\{1,2,...,n\},m)=0f({1,2,...,n},m)=0,不可能所有边都加完了还不联通。
所以,有:
ans=1m+1∑i=0mf({1,2,...,n},i)(mi)ans=\frac{1}{m+1}\sum_{i=0}^m\frac{f(\{1,2,...,n\},i)}{\binom mi} ans=m+11i=0∑m(im)f({1,2,...,n},i)
code
#include <bits/stdc++.h>
using namespace std;
#define double long double
#define int long long
int n, m;
int cnt[1 << 10];
double c[50][50];
int f[1 << 10][50], g[1 << 10][50];
struct node { int u, v; }E[50];signed main() {scanf( "%lld %lld", &n, &m );for( int i = 1;i <= m;i ++ ) scanf( "%lld %lld", &E[i].u, &E[i].v );for( int s = 0;s < (1 << n);s ++ )for( int i = 1;i <= m;i ++ )if( (s >> E[i].u - 1 & 1) and (s >> E[i].v - 1 & 1) )cnt[s] ++;for( int i = 0;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];}for( int s = 0;s < (1 << n);s ++ )for( int i = 0;i <= cnt[s];i ++ ) {for( int t = s;t;t = (t - 1) & s )if( t & (s & -s) )for( int j = 0;j <= min( cnt[t], i );j ++ )f[s][i] += g[t][j] * c[cnt[s ^ t]][i - j];g[s][i] = c[cnt[s]][i] - f[s][i];}double ans = 0;for( int i = 0;i <= m;i ++ ) ans += 1.0 / (m + 1) * f[(1 << n) - 1][i] / c[m][i];printf( "%Lf\n", ans );return 0;
}