1. 程式人生 > >bzoj 4770 圖樣 - 概率與期望 - 動態規劃

bzoj 4770 圖樣 - 概率與期望 - 動態規劃

題目傳送門

  傳送門I

  傳送門II

題目大意

  有一個$n$個點的完全圖,每個點的權值是$[0, 2^{m})$中的隨機整數,兩點間的邊的權值是兩點點權的異或和,問它的最小異或生成樹的邊權和的期望。

  考慮求最大異或生成樹的分治做法,每次按最高位分成$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 << '\n'; 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\n", i, j, cnt, tmp); 99 val = add(val, mul(tmp, comb[i][cnt])); 100 } 101 // printf("f[%d][%d] = %d\n", i, j, f[i][j]); 102 } 103 for (int i = 1; i <= n; i++) { 104 for (int j = 0; j <= m; j++) { 105 printf("\tf[%d][%d] = %d;\n", 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 }