Step to One
Steps to One
https://ac.nowcoder.com/acm/problem/113552
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); }
有的没的 文章被收录于专栏
RT,有的没的