LOJ2229. 「BJOI2014」想法(随机化)

题目链接

https://loj.ac/problem/2229

题解

评分标准提示我们可以使用随机化算法。

首先,我们为每一道编号在 ([1, m]) 以内的题目(这些题目也对应了 (m) 个初始的想法)赋一个 ([0, d]) 以内的随机权值。接下来,我们可以通过 (O(n)) 的递推来求出每一道编号在 ((m, n]) 以内的题目所包含的所有想法对应权值的最小值。记第 (i(i > m)) 道题目包含 (x_i) 个不同的想法,且这些想法对应权值的最小值为 (w_i),那么有 (w_i) 的期望值为 (frac{d}{x_i + 1})

我们试着证明一下上述结论。在此之前,我们先思考一个值域较小但更为普遍的问题:在 ([0, 1]) 内选择 (x) 个随机变量(变量之间互相独立,且在 ([0, 1]) 内均匀随机),求选出的这 (x) 个变量中第 (k) 小值的期望。

我们将该问题做一个转化:求选出的这 (x) 个变量中第 (k) 小值的期望,等价于求再在 ([0, 1]) 内选择一个随机变量,求选出的这个变量小于之前选出的 (x) 个变量中第 (k) 小值的概率。

经过转化之后的问题显然就很好做了。我们考虑按照数值从小到大给这 (x + 1) 个变量赋上排名。忽略变量相等的情况,那么这 (x + 1) 个变量的排名构成了一个 (x + 1) 的排列,且显然,产生各个排列的概率是相同的。(x + 1) 的全排列数为 ((x + 1)!),我们考虑用合法的排列数除以全排列数来求概率,这样,问题转化为了求共有多少种 (x + 1) 的排列满足排列的最后一个位置的值不超过 (k)。显然合法的排列总数为 (k imes x!)。因此概率即为 (frac{k imes x!}{(x + 1)!} = frac{k}{x + 1}),那么可以得到在 ([0, 1]) 内选出的 (x) 个随机变量中第 (k) 小值的期望也为 (frac{k}{x + 1})

这个结论其实被直接放在了[ZJOI2015]地震后的幻想乡一题的提示中。

这样,当随机权值的值域为 ([0, d]) 时,选出 (x) 个随机权值的最小值 (w) 的期望即为 (frac{1}{x + 1} imes d)。若求得 (w) 的期望 (E),那么可得 (x = frac{d}{E} - 1)

对于第 (i) 个想法,我们可以通过多次随机化求平均数来得到 (w_i) 的期望的近似值。设随机化的次数为 (T),那么总时间复杂度为 (O(Tn))

代码

#include<bits/stdc++.h>

using namespace std;

const int N = 1e6 + 10;

int n, m, from[N][2], a[N];
double answer[N];

int main() {
  scanf("%d%d", &n, &m);
  int M = 100000000 / n;
  for (int i = m + 1; i <= n; ++i) {
    scanf("%d%d", &from[i][0], &from[i][1]);
  }
  for (int tt = 1; tt <= M; ++tt) {
    for (int i = 1; i <= m; ++i) {
      a[i] = rand();
    }
    for (int i = m + 1; i <= n; ++i) {
      a[i] = min(a[from[i][0]], a[from[i][1]]);
      answer[i] += (double) a[i] / M;
    }
  }
  for (int i = m + 1; i <= n; ++i) {
    answer[i] = RAND_MAX / answer[i] - 1;
    printf("%.0lf
", answer[i]);
  }
  return 0;
}
原文地址:https://www.cnblogs.com/ImagineC/p/10267873.html