zoukankan      html  css  js  c++  java
  • BZOJ4944 泳池 解题报告

    题目描述

    有一个 \(n\) 行无穷列的海域,每个格子有 \(q\) 的概率安全, \(1-q\) 的概率不安全。从中框出一个面积最大的矩形,满足以下两个条件:

    (1)矩形内的格子均安全;

    (2)矩形必须紧靠海域的最左端(即包含第一列的格子)。

    问最大面积为 \(k\) 的概率是多少。这里在模998244353意义下计算,给出 \(q\) 的分数表示形式 \(q=x/y\) 。

    数据规模: \(1 \le n \le 10^9, 1 \le k \le 5 \times 10^4\) 。

    简要题解

    该问题等价于不超过 \(k\) 的概率减去不超过 \(k-1\) 的概率。

    设 \(dp[i][j]\) 表示海域有 \(i\) 行,且第1列到第 \(j\) 列全安全的条件下,选取的最大面积不超过 \(k\) 的概率。我们要求的即为 \(dp[n][0]\) 。

    初始条件和边界条件如下: \(dp[i][j]=0,i \times j>k,dp[0][j]=1\)

    考虑 \(dp[i][j]\) 从谁转移。有两种情况:

    情况一:第 \(j+1\) 列全安全;

    情况二:第 \(j+1\) 列存在不安全的格子,则可以枚举第 \(j+1\) 列最靠上的不安全位置 \(t\) ,那么当且仅当上面 \(t-1\) 行面积不超过 \(k\) (概率 \(dp[t - 1][j + 1]{q^{t - 1}}\) ),且下面 \(i-t\) 行面积不超过 \(k\) (概率 \(dp[i-t][j]\) )时,整个地图选取的最大面积才不超过 \(k\) 。因而转移方程:

    \[dp[i][j] = dp[i][j + 1]{q^i} + \sum\limits_{t = 1}^i {dp[t - 1][j + 1]{q^{t - 1}}(1 - q)dp[i - t][j]} \]

    对于dp的复杂度,当 \(j=0\) 时暂时只求出 \(i \le k\) 的项, \(j>0\) 时只需求 \(ij \le k\) 的项,写成求和式为

    \[\Theta ({k^2}) + \sum\limits_{j = 1}^k {\sum\limits_{i = 0}^{k/j} i } = \Theta({k^2})\]

    考虑进一步优化。注意到转移是一个和自身相关的卷积,因而考虑生成函数。

    设 \(F_j(x)\) 是 \(dp[i][j]\) 的生成函数, \(G_j(x)\) 是 \(dp[i][j]{q^i}\) 的生成函数,则\[{F_j}(x) = {G_{j + 1}}(x) + (1 - q)x{G_{j + 1}}(x){F_j}(x)\]得\[{F_j}(x) = \frac{{{G_{j + 1}}(x)}}{{1 - (1 - q)x{G_{j + 1}}(x)}}\]

    按 \(j\) 递减顺序交替求 \({F_j}(x),{G_j}(x)\) ,时间复杂度为

    \[\sum\limits_{j = 1}^k {\left\lfloor {\frac{k}{j}} \right\rfloor \log \left\lfloor {\frac{k}{j}} \right\rfloor } \approx k\sum\limits_{j = 1}^k {\frac{{\log k - \log j}}{j}} \approx k\log k\ln k - \frac{1}{2}k\log k\ln k = \Theta (k{\log ^2}k)\]

    接下来考虑 \(dp[i][0](i>k)\) 。原始状态转移方程可化简为 \[dp[i][0] = \sum\limits_{t = 1}^{k + 1} {dp[t - 1][1]{q^{t - 1}}(1 - q)dp[i - t][0]}\] 不难发现是常系数齐次线性递推。因而可在 \(k \log k \log n\) 时间复杂度解决。

    总时间复杂度 \(k \log k(\log k+\log n)\) 。

    核心代码

    已略去冗长的多项式模板以及线性递推模板。

     1 int q, qi[50001];
     2 int solve(int n, int k)
     3 {
     4     Poly f(1, 1), g(1, 1), a;
     5     for (int j = k; j >= 0; j--){
     6         int d = j ? k / j : k;
     7         if (j == 0)a = g * (q - 1);
     8         f.resize(d + 1); g.resize(d + 1);
     9         for (int i = 1; i <= d; i++)
    10             f[i] = mul(g[i - 1], q - 1);
    11         f = g * inv(f);
    12         for (int i = 1; i <= d; i++)
    13             g[i] = mul(f[i], qi[i]);
    14     }
    15     reverse(a.begin(), a.end());
    16     a.push_back(1);
    17     return linear_recursion(a, f, n);
    18 }
    19 int main()
    20 {
    21     int n, k, x, y;
    22     scanf("%d%d%d%d", &n, &k, &x, &y);
    23     q = mul(x, power(y, MOD - 2));
    24     qi[0] = 1;
    25     for (int i = 1; i <= k; i++)
    26         qi[i] = mul(qi[i - 1], q);
    27     printf("%d", sub(solve(n, k), solve(n, k - 1)));
    28 }

    总结

    非常喜欢这类蕴含着思维难度,但是代码量极少的题目。真心佩服出题人系列~

    参考博客

    http://blog.csdn.net/ez_yww/article/details/78679459

  • 相关阅读:
    SpringBoot配置默认日志logback
    SpringBoot静态资源映射、拦截器、过滤器使用
    windows系统安装两个Mysql服务
    SpringBoot使用Jsp开发Web项目
    SpringBoot使用Thymeleaf开发Web项目
    SpringBoot启动原理
    javascript Date
    UDP发送数据测试
    自动迁移
    EF 自测例子
  • 原文地址:https://www.cnblogs.com/zbh2047/p/8580216.html
Copyright © 2011-2022 走看看