考虑求最大异或生成树的分治做法,每次按最高位分成$V_0,V_1$两个集合(如果不行,那么这一层就不管)。
然后再中间选一条最小边连接两个集合。两个集合分别再分治下去。
现在我们希望求到中间这条最小边的边权的期望。
直接求不好求,考虑换个方式统计。
设$h_{n,m,bit,lim}$表示在第$bit + 1$位将图分成$X,Y$两个集合,其中$|X| = n, |Y| = m$,两个集合间的所有边的边权都大于等于$lim$的方案数(或概率)。
转移的时候讨论一下
- $lim$在第$bit$位为0
- 如果$X,Y$中的点在$bit$位,一个集合中全为0,另一个集合中全为1,那么剩下的位可以任意填。
- 否则枚举$X,Y$中分别有多少个点在第$bit$位为1,然后将集合又分为$X_0, X_1, Y_0, Y_1$,显然最小边连在$X_0, Y_0$之间或者$X_1,Y_1$之间,这是一个子问题,可以通过这个子问题的答案转移,再乘上组合数。
- $lim$在第$bit$位为1
- 此时$X,Y$中的点在$bit$位一定满足一个集合中全为0,另一个集合中全为1,直接通过它转移。
统计期望或每种情况的边权和的时候做一个和就求出来了。
设$f_{i, j}$表示有一个$i$个点的完全图,每个点的权值是$[0, 2^{j})$中的随机整数时的所有方案的答案和。
显然能通过枚举$X_1$集合的大小来转移。
复杂度令人绝望(据说常数小就能卡过去)。但是$n,m$都比较小,打个表交上去就过了。
Code
1 #include <iostream> 2 #include <cstdlib> 3 #include <cstring> 4 #include <cstdio> 5 using namespace std; 6 typedef bool boolean; 7 8 const int N = 55, M = 9, S = 1 << M; 9 const int Mod = 258280327; 10 11 int add(int a, int b) { 12 return ((a += b) >= Mod) ? (a - Mod) : (a); 13 } 14 15 int sub(int a, int b) { 16 return ((a -= b) < 0) ? (a + Mod) : (a); 17 } 18 19 int mul(int a, int b) { 20 return a * 1ll * b % Mod; 21 } 22 23 int qpow(int a, int p) { 24 int pa = a, rt = 1; 25 for ( ; p; p >>= 1, pa = mul(pa, pa)) 26 if (p & 1) 27 rt = mul(rt, pa); 28 return rt; 29 } 30 31 int inv(int a, int n) { 32 return qpow(a, n - 2); 33 } 34 35 int n, m; 36 int f[N][N]; 37 int comb[N][N]; 38 int pow2[N * M]; 39 int h[N][N][M][S]; 40 41 inline void init() { 42 scanf("%d%d", &n, &m); 43 comb[0][0] = 1; 44 for (int i = 1; i <= n; i++) { 45 comb[i][0] = comb[i][i] = 1; 46 for (int j = 1; j < i; j++) 47 comb[i][j] = add(comb[i - 1][j - 1], comb[i - 1][j]); 48 } 49 pow2[0] = 1; 50 for (int i = 1; i <= (n + 1) * (m + 1) + 1; i++) 51 pow2[i] = add(pow2[i - 1], pow2[i - 1]); 52 } 53 54 int dp(int n, int m, int bit, int lim) { 55 if (!bit) 56 return !lim; 57 if (!n || !m) 58 return pow2[(n + m) * bit]; 59 int& rt = h[n][m][bit][lim]; 60 if (~rt) 61 return rt; 62 if ((lim >> (bit - 1)) & 1) { 63 rt = dp(n, m, bit - 1, lim ^ (1 << (bit - 1))); 64 rt = add(rt, rt); 65 } else { 66 rt = pow2[(n + m) * (bit - 1) + 1]; 67 for (int x = 0; x <= n; x++) 68 for (int y = 0; y <= m; y++) { 69 if ((!x && y == m) || (!y && x == n)) 70 continue; 71 int a = dp(x, y, bit - 1, lim); 72 int b = dp(n - x, m - y, bit - 1, lim); 73 rt = add(rt, mul(mul(a, b), mul(comb[n][x], comb[m][y]))); 74 } 75 } 76 return rt; 77 } 78 79 int g(int n, int m, int bit) { 80 int rt = 0; 81 for (int i = 1; i < pow2[bit]; i++) 82 rt = add(rt, dp(n, m, bit, i)); 83 // cerr << n << " " << m << " " << rt << " " << bit << ' '; 84 return rt; 85 } 86 87 inline void solve() { 88 memset(h, -1, sizeof(h)); 89 for (int i = 1; i <= n; i++) 90 for (int j = 1; j <= m; j++) { 91 int& val = f[i][j]; 92 val = add(f[i][j - 1], f[i][j - 1]); 93 for (int cnt = 1, tmp; cnt < i; cnt++) { 94 tmp = mul(f[cnt][j - 1], pow2[(i - cnt) * (j - 1)]); 95 tmp = add(tmp, mul(f[i - cnt][j - 1], pow2[cnt * (j - 1)])); 96 tmp = add(tmp, g(cnt, i - cnt, j - 1)); 97 tmp = add(tmp, pow2[(i + 1) * (j - 1)]); 98 // printf("tmp %d %d %d= %d ", i, j, cnt, tmp); 99 val = add(val, mul(tmp, comb[i][cnt])); 100 } 101 // printf("f[%d][%d] = %d ", i, j, f[i][j]); 102 } 103 for (int i = 1; i <= n; i++) { 104 for (int j = 0; j <= m; j++) { 105 printf(" f[%d][%d] = %d; ", i, j, mul(f[i][j], inv(pow2[i * j], Mod))); 106 } 107 } 108 } 109 110 int main() { 111 freopen("young.txt", "w", stdout); 112 init(); 113 solve(); 114 return 0; 115 }