• 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 << '
    ';
     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 }
  • 相关阅读:
    WCF学习笔记1发布使用配置文件的服务
    Oracle(Hierarchical Queries)层级查询
    Telerik UI For WinForms关于RadGridView的列排序
    C#异步显示工作进度
    CodeSmith连接Oracle
    Access数据导入SQLServer2008R2
    Oracle (RANK) 排名函数
    C#INotifyPropertyChanged(解决数据绑定的界面刷新问题)
    Code Complete读书笔记03
    C++ Primer Plus读书笔记08
  • 原文地址:https://www.cnblogs.com/yyf0309/p/10121911.html
Copyright © 2020-2023  润新知