CF1139D Step to One

题目描述

你手中有集合 ,然后每一次操作你都会从数集中等概率抽取一个数放到新的序列中,直到新的序列的 的值为

求你的期望操作次数

分析

这道题十分的精妙,它集合了数学期望还有莫比乌斯反演,十分考验 的基础能力

数学期望

首先知道, 表示对于事件 的数学期望, 表示事件 的概率

那么有:

这个步骤,就是期望步数(概率 * 步数),显然就是期望公式

其中 表示达到 花费的步数

那么重点就是这个该如何处理了

想要 那么可以理解为长度为 时的

所以可以有如下化简:

莫比乌斯反演

这个和之前的某道题有点像,所以化简过程就不写了,所以就可以得到

这一步也很显然

那么现在就可以带回数学期望的式子里,可以得到:

那么最后数论分块 +。线性筛逆元一下就可以了,时间复杂度 ,但是其实也是可以 做的,毕竟数据范围很小

Code

#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

typedef long long ll;
const int maxn = 1e5 + 10;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;

inline int __read()
{
    int x(0), t(1);
    char o (getchar());
    while (o < '0' || o > '9') {
        if (o == '-') t = -1;
        o = getchar();
    }
    for (; o >= '0' && o <= '9'; o = getchar()) {
        x = (x << 1) + (x << 3) + (o ^ 48);
    }
    return x * t;
}

int mu[maxn], inv[maxn];
int pr[maxn], cnt, ans(1);
bool vis[maxn];

inline void init(int maxn)
{
    mu[1] = inv[1] = 1;
    for (int i = 2; i <= maxn; ++i) {
        if (!vis[i]) pr[++cnt] = i, mu[i] = - 1;
        for (int j = 1; j <= cnt && i * pr[j] <= maxn; ++j) {
            vis[i * pr[j]] = 1;
            if (i % pr[j]) mu[i * pr[j]] = -mu[i];
            else break;
        }
    }

    for (int i = 2; i <= maxn; ++i) {
        mu[i] += mu[i - 1];
        inv[i] = 1ll * (mod - mod / i) * inv[mod % i] % mod;
    }
}

int main()
{
    int n = __read();
    init(n);
    for (int l = 2, r; l <= n; l = r + 1) {
        r = n / (n / l);
        int temp = n / l;
        ans = ((ans - 1ll * (mu[r] - mu[l - 1]) % mod * temp % mod * inv[n - temp] % mod) % mod + mod) % mod;
    }
    printf ("%d\n", ((ans % mod) + mod) % mod);
}