题目链接:D. Steps to One
题意:
给你一个整数m(1≤m≤100000),刚开始序列a为空,循环进行以下操作:
- 随机在[1,m]选择一个整数x,将x加入序列a尾部
- 计算集合a的最小公倍数
- 如果
,结束。
- 否则,跳到1.
问循环结束时序列a的长度期望,对1e9+7取模。
题目分析:
设表示
时,到达
时所需的期望循环次数
那么所求答案可以表示为:
1代表第一次取出一个数字,求和是选到第一个数字之后的期望次数*概率
假设当前,下一个数字为
,那么
,我们可以得到:
将式子左右同乘,设
,则有:
对内部的和式进行化简:
代回原式:
将F(n)移到同一边得到:
计算这个式子需要枚举的因子
,对于每个
再次枚举因子
,复杂度玄学,O(能过)
代码:
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const ll mod = 1e9+7;
const int MAXN = 100005;
int m;
ll F[MAXN], inv[MAXN];
int mu[MAXN], p[MAXN], tot;
bool nprime[MAXN];
struct node{
int fac, next;
}e[MAXN*100];
int head[MAXN], cnt;
inline void inser(int k, int d){//d|k
e[cnt] = (node){d,head[k]}, head[k] = cnt++;
}
inline void add(int &a, ll b){
a += b; if(a >= mod) a -= mod;
}
void init(){
tot = cnt = 0;
//mu函数
mu[1] = 1; nprime[0] = nprime[1] = 1;
for(int i = 2;i<MAXN;i++){
if(!nprime[i]) mu[i] = -1, p[tot++] = i;
for(int j = 0;j<tot && i*p[j] < MAXN;j++){
nprime[i*p[j]] = 1;
if(i%p[j]==0){
mu[i*p[j]] = 0;
break;
}
mu[i*p[j]] = -mu[i];
}
}
//处理因子d|n && d != n
memset(head, -1, sizeof head);
for(int i = 2;i<(MAXN>>1);i++)
for(int j = (i<<1);j<MAXN;j+=i)
inser(j,i);
//逆元
inv[1] = 1;
for (ll i = 2; i < MAXN; ++i)
inv[i] = 1ll*(mod-mod/i)*inv[mod%i]%mod;
//F[i]表示当前gcd为i,到达gcd为1所需的期望长度
F[1] = 0;
for(int i = 2;i <= m;i++){
int tmp = 0;
for(int j = head[i];~j;j = e[j].next){
int d = e[j].fac;
int x = m/d;
int tmpp = 1ll*mu[i/d]*(x/(i/d))%mod;
for(int k = head[i/d];~k;k = e[k].next)
add(tmpp, 1ll*mu[e[k].fac]*(x/e[k].fac)%mod);
add(tmpp, x);
add(tmp, 1ll*tmpp*F[d]%mod);
}
add(tmp, m);
F[i] = 1ll*tmp*inv[m-(m/i)]%mod;
}
}
int main() {
scanf("%d",&m);
init();
int ans = 0;
for(int i = 1;i<=m;i++)add(ans,F[i]);
ans = 1ll*ans*inv[m]%mod;
add(ans, 1);
printf("%d\n",ans);
return 0;
}