测评会员优惠活动进行中 · 开通 VIP,有效期内测评不限次 VIP 优惠中 · 测评不限次 立即查看

A25055. 可怜的简单题

填空题 困难

题目描述

可怜的简单题

题目描述

九条可怜今年出了一道简单题 —— 打算按照如下的方式生成一个随机的整数数列 A:

1 . 最开始,数列 A 为空。

2 . 可怜会从区间 [1,n] 中等概率随机一个整数 i 加入到数列 A 中。

3 . 如果不存在一个大于 1 的正整数 w,满足 A 中所有元素都是 w 的倍数,数组 A 将会作为随机生成的结果返回。否则,可怜将会返回第二步,继续增加 A 的长度。

现在,可怜告诉了你数列 n 的值,她希望你计算返回的数列 A 的期望长度。

输入

输入一行两个整数 n, p (1 ≤ n ≤ 1011, n < p ≤ 1012),p 是一个质数。

输出

在一行中输出一个整数,表示答案对 p 取模的值。具体来说,假设答案的最简分数表示为 x/y,你需要输出最小的非负整数 z 满足 y × z ≡ x mod p。

样例输入

样例1:

2 998244353

样例2:

100000000 998244353

样例输出

样例1:

2

样例2:

3054970

参考答案

#include <bits/stdc++.h> using namespace std; typedef long long ll; const int Lim=2.2e7, MAXN=Lim+10; ll n, p; inline ll fmul(ll a,ll x) { return (__int128)a*x%p; } inline ll fpow(ll a,ll x) { ll ans=1; for(;x;x>>=1,a=fmul(a, a) ) if(x&1) ans=fmul(ans, a); return ans; } inline ll inv(ll a) { return fpow(a, p-2); } ll smu[MAXN]; int prime[MAXN], cntprime; bool nprime[MAXN]; unordered_map<ll, ll> M; inline ll summu(ll n){ if(n<=Lim) return smu[n]; if(M.count(n)) return M[n]; for(ll r=n, l, d;r>=1; r=l-1){ d=n/r; l=n/(d+1)+1; if(d<=Lim||M.count(d)) continue; ll tmp=1; for(ll ll=2, rr, dd;ll<=d;ll=rr+1){ dd=d/ll; rr=d/dd; if(dd<=Lim) tmp-=fmul(rr-ll+1, smu[dd]); else tmp-=fmul(rr-ll+1, M[dd]); } M[d]=(tmp%p+p)%p; } return M[n]; } inline void sieve(){ smu[1]=1; for(int i=2;i<=Lim;++i){ if(!nprime[i]) prime[++cntprime]=i, smu[i]=-1; for(int j=1;j<=cntprime;++j) if(i*prime[j]>Lim) break; else if(i%prime[j]){ nprime[i*prime[j]]=1; smu[i*prime[j]]=-smu[i]; } else{ nprime[i*prime[j]]=1; smu[i*prime[j]]=0; break; } } for(int i=2;i<=Lim;++i){ smu[i]+=smu[i-1]; if(smu[i]>=p) smu[i]-=p; else if(smu[i]<0) smu[i]+=p; } } int main(){ ios::sync_with_stdio(0); cin.tie(0); cout.tie(0); cin>>n>>p; sieve(); ll ans=0; summu(n); for(ll l=2, r, d, tmp=n%p;l<=n;l=r+1){ d=n/l; r=n/d; ans+=fmul( summu(r)-summu(l-1) , inv(d-tmp+p) ); } ans=(ans%p+p)%p; ans=( fmul(ans, n)+summu(n)+p )%p; cout<<ans; cout.flush(); return 0; }
上一题 下一题