嘘~ 正在从服务器偷取页面 . . .


CF618G Combining Slimes 题解

题目链接:Combining Slimes

题意

有一个 $1 \times n$ 的网格,你将对这个网格进行 $n$ 次操作。

每次从右端插入一个数 $1$ 或 $2$ 。当有两个相邻的数字 $x$ 时,他们会合并为一个数字 $x+1$ 。

插入 $1$ 的概率为 $p$ ,插入 $2$ 的概率为 $1-p$ 。(原题给的是整数 $p_0=p\times 10^{9}$ )

求无法合并时,$n$ 个格子中所有数的和的期望。浮点输出,要求误差小于 $10^{-4}$ 。

输入:一行,$n,p_0$ 。输出:一行,答案。数据范围:$1\le n,p_0 \le 10^9$ 。

由于每个数都可能出现,根据期望的线性性,所以我们可以分别计算每个数的贡献。

设 $a_{i,j}$ 为用 $i$ 个格子且最左边的那一格为 $j$ 的概率,则

根据经验,$a_{i,j}$ 可近似为 $(1-p)^{2^{j-1}}$ ,当 $j = 50$ 时这个值最大为 $10^{-10^{5.39}}$ 。

真是小到连高精度浮点数都汗流浃背了$😅$。因此不妨记 $m=50$ ,此后对于 $j > m$ 我们一律视作 $0$ 。

设 $A_{i,j}$ 为用 $i$ 个格子且最左边的那一格为 $j$ 且它不会被合并的概率,则

设 $f_{i,j}$ 为最终序列中右 $i$ 位中最左侧的格子是 $j$ 时,这 $i$ 位的期望和,则

当 $j=1$ 时,下一个插入的可能比 $j$ 大。

不过,注意到下一个插入的一定是 $2$ 。考虑特别处理这种情况。

设 $b_{i,j}$ 为用 $i$ 个格子且最左边的那一格为 $j$ 且第一次插入的是 $2$ 时的概率。

这里转移其实很简单,我们只需要考虑 $2$ 是最先加入的,那么

设 $B_{i,j}$ 为用 $i$ 个格子且最左边的那一格为 $j$ 且它不会被合并且第一次插入的是 $2$ 时的概率,则

那么

最终答案就是

由于 $i > m$ 时 $A_{i,j} \approx A_{m,j}$ ,于是答案为

那么我们只需要处理出 $f_{n,j}$ 即可。

注意到 $f$ 的转移由上一项递推而来,且 $m = 50$ ,考虑矩阵快速幂

时间复杂度 $\mathcal{O}(m^3 \log n)$

代码:

#include <bits/stdc++.h>
using namespace std;
// #define int long long
// #define INF 0x3f3f3f3f3f3f3f3f
typedef long long ll;
typedef long double lf;
typedef vector< vector<lf> > mat;
void up(int &x, int y) { x < y ? x = y : 0; }
void down(int &x, int y) { x > y ? x = y : 0; }
#define M ((int)(66))

lf a[M][M], b[M][M], c[M][M], d[M][M], dp[M][M];
mat mul(mat &A, mat &B)
{
    int n = A.size(), m = A[0].size(), p = B[0].size();
    mat C(n, vector<lf>(p));
    for(int k = 0; k < m; k++)
        for(int i = 0; i < n; i++)
            for(int j = 0; j < p; j++) C[i][j] += A[i][k] * B[k][j];
    return C;
}
mat qpow(mat A, int k)
{
    int n = max(A.size(), A[0].size());
    mat B(n, vector<lf>(n));
    for(int i = 0; i < n; i++) B[i][i] = 1;
    for(; k; k >>= 1, A = mul(A, A))
        if(k & 1) B = mul(B, A);
    return B;
}
signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0); cout.tie(0);
    // freopen("check.in","r",stdin);
    // freopen("check.out","w",stdout);
    int n; lf p; cin >> n >> p; p /= 1e9; const int m = 50;
    a[1][1] = p; a[1][2] = 1 - p; b[1][2] = 1 - p;
    for(int i = 2; i <= m; i++)
    {
        a[i][1] = p; a[i][2] = 1 - p + p * p; b[i][2] = 1 - p;
        for(int j = 3; j <= m; j++)
        {
            a[i][j] = a[i][j - 1] * a[i - 1][j - 1];
            b[i][j] = b[i][j - 1] * a[i - 1][j - 1];
        }
    }
    for(int i = 1; i <= m; i++)
        for(int j = 1; j <= m; j++)
        {
            c[i][j] = a[i][j] * (1 - a[i - 1][j]);
            d[i][j] = b[i][j] * (1 - a[i - 1][j]);
        }
    dp[1][1] = 1; dp[1][2] = 2;
    for(int i = 2; i <= m; i++)
    {
        lf sum = 0;
        for(int k = 2; k <= m; k++)
        { 
            dp[i][1] += dp[i - 1][k] * d[i - 1][k];
            sum += d[i - 1][k];
        }
        dp[i][1] = 1 + dp[i][1] / sum;
        for(int j = 2; j <= 50; j++)
        {
            sum = 0;
            for(int k = 1; k < j; k++)
            {
                dp[i][j] += dp[i - 1][k] * c[i - 1][k];
                sum += c[i - 1][k];
            }
            dp[i][j] = j + dp[i][j] / sum;
        }
    }
    if(n <= m)
    {
        lf res = 0;
        for(int i = 1; i <= n + 1; i++) res += dp[n][i] * c[n][i];
        cout << fixed << setprecision(12) << res << '\n'; return 0;
    }
    static mat A(5, vector<lf>(m + 5)), B(m + 5, vector<lf>(m + 5));
    A[0][0] = B[0][0] = 1;
    for(int i = 1; i <= m; i++) A[0][i] = dp[m][i];
    lf sum = 0;
    for(int j = 2; j <= m; j++) { B[j][1] += d[m][j], sum += d[m][j]; }
    for(int j = 2; j <= m; j++) B[j][1] /= sum;
    B[0][1] = 1;
    for(int i = 2; i <= m; i++)
    {
        sum = 0;
        for(int j = 1; j < i; j++) { B[j][i] += c[m][j], sum += c[m][j]; }
        for(int j = 1; j < i; j++) B[j][i] /= sum;
        B[0][i] = i;
    }
    A = mul(A, B = qpow(B, n - m));
    lf res = 0;
    for(int i = 1; i <= m; i++) res += A[0][i] * c[m][i];
    cout << fixed << setprecision(12) << res << '\n';
    return 0;
}

参考文献

[1] https://www.luogu.com.cn/article/gja1htf1


题外话

下次研究性学习我就放这个题。

不过先放图片。


文章作者: q779
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-ND 4.0 许可协议。转载请注明来源 q779 !
评论
  目录