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 }

 

posted @ 2018-12-14 22:00  阿波罗2003  阅读(424)  评论(0编辑  收藏  举报