CF1139D

CF1139D Step to One

题目描述

你手中有集合 \(\{1,2,3\cdots,n\}\),然后每一次操作你都会从数集中等概率抽取一个数放到新的序列中,直到新的序列的 \(\gcd\) 的值为 \(1\)

求你的期望操作次数

分析

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

数学期望

首先知道,\(E(x)\) 表示对于事件 \(x\) 的数学期望, \(P(x)\) 表示事件 \(x\) 的概率

那么有: \[ \begin{aligned} E(\gcd=1)&=\sum_{i\ge 1}E(len=i) \\&=\sum_{i\ge1}P(len=i)\times i\\ &=\sum_{i\ge1}P(len=i)\sum_{j=1}^i1\\ &=\sum_{j\ge1}\sum_{i\ge j}P(len = i)\\ &=\sum_{j\ge1}P(len \ge j)\\ &=\sum_{j\ge1}P(len=j)+\sum_{j\ge1}P(len > j) \\&=1+\sum_{j\ge1}P(len > j) \end{aligned} \] 这个步骤,就是期望步数(概率 * 步数),显然就是期望公式\(E(x+y)=E(x)+E(y)\)

其中 \(len\) 表示达到 \(\gcd=1\) 花费的步数

那么重点就是这个\(\sum_{j\ge1}P(len>j)\)该如何处理了

想要 \(len>j\) 那么可以理解为长度为 \(j\) 时的 \(\gcd>1\)

所以可以有如下化简: \[ \begin{aligned} P(len>j)&={j\ge1}P(\gcd_{i=1}^ja_i>1)\\ &=1-P(\gcd_{i=1}^ja_i=1)\\ &=1-\frac1{n^j}\sum_{a_1=1}^n\sum_{a_2=1}^n\cdots\sum_{a_j=1}^n[\gcd_{i=1}^ja_i==1] \end{aligned} \]

莫比乌斯反演

这个和之前的某道题有点像,所以化简过程就不写了,所以就可以得到 \[ \begin{aligned} P(len>j)&=1-\frac1{n^j}\sum_{a_1=1}^n\sum_{a_2=1}^n\cdots\sum_{a_j=1}^n[\gcd_{i=1}^ja_i==1]\\ &=1-\frac1{n^j}\sum_{d=1}^n\mu(d)[\frac nd]^j\\ &=-\frac1{n^j}\sum_{d=2}^n\mu(d)[\frac nd]^j \end{aligned} \] 这一步也很显然

那么现在就可以带回数学期望的式子里,可以得到: \[ \begin{aligned} E(\gcd=1)&=1+\sum_{j\ge1}P(len>j)\\ &=1-\sum_{j\ge1}\frac1{n^j}\sum_{d=2}^n\mu(d)[\frac nd]\\ &=1-\sum_{d=2}^n\mu(d)\sum_{j\ge1}(\frac {[\frac nd]}n)^j\\ &=1-\sum_{d=2}^n\mu(d)\frac{[\frac nd]}{n-[\frac nd]} \end{aligned} \] 那么最后数论分块 + 线性筛逆元一下就可以了,时间复杂度 \(O(\sqrt n)\),但是其实也是可以 \(O(n)\) 做的,毕竟数据范围很小

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
#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);
}