洛谷 P3768 简单的数学题 题解

$\text{Description}$

给定正整数 $n,p$,求

$n\le 10^{10},5\times 10^8\le p\le 1.1\times 10^9$。

$\text{Solution}$

欧拉反演:

交换求和次序:

背后那一坨可以 $O(1)$ 算出,所以问题变为计算 $f(n)=\varphi(n)n^2$ 的前缀和。

那我们知道 $\varphi(n)$ 的前缀和就是卷上一个 $\operatorname{1}$ 变成 $\operatorname{Id}$ 就可以杜教筛算了。

后面这个 $n^2$ 怎么算?

你看狄利克雷卷积的过程中是一个 $d$ 和一个 $n/d$ 相乘,它们直接就成常数了。

那么肯定选择 $g(n)=n^2$,我们知道 $g$ 的前缀和可以 $O(1)$ 算,那么还有:

也可以 $O(1)$ 算前缀和。

杜教筛跑一下就出来了。

$\text{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
59
60
61
62
63
64
65
66
67
68
69
#include<bits/stdc++.h>
#define REG register
using namespace std;
const int N=5e6;
typedef long long ll;
inline void read(int& x){
static char c;
while(!isdigit(c=getchar()));x=c^48;
while(isdigit(c=getchar()))x=(x*10)+(c^48);
}
inline void read(ll& x){
static char c;
while(!isdigit(c=getchar()));x=c^48;
while(isdigit(c=getchar()))x=(x*10)+(c^48);
}

int p;
ll n;

int _2,_6;

inline int Pow(int a,int b){
int res=1;
while(b){
if(b&1) res=1ll*res*a%p;
a=1ll*a*a%p,b>>=1;
}
return res;
}

inline int _(ll n){n%=p;return (int)(n*(n+1)%p*_2%p);}
inline int __(ll n){n%=p;return (int)(n*(n+1)%p*(2*n%p+1)%p*_6%p);}
inline int ___(ll n){int t=_(n%p);return (int)(1ll*t*t%p);}

int Phi[N+10],Prm[N+10],vis[N+10],cnt;
inline void Euler(){
Phi[1]=1,_2=Pow(2,p-2),_6=Pow(6,p-2);
for(REG int i=2;i<=N;++i){
if(!vis[i]) Phi[i]=i-1,Prm[++cnt]=i;
for(REG int j=1;j<=cnt&&i*Prm[j]<=N;++j){
vis[i*Prm[j]]=1;
if(i%Prm[j]==0){Phi[i*Prm[j]]=1ll*Phi[i]*Prm[j]%p;break;}
Phi[i*Prm[j]]=1ll*Phi[i]*(Prm[j]-1)%p;
}
}
for(REG int i=2;i<=N;++i) Phi[i]=(1ll*i*i%p*Phi[i]%p+Phi[i-1])%p;
}

map<ll,int> SPhi;
inline int Seive(ll n){
if(n<=N) return Phi[n];
if(SPhi.find(n)!=SPhi.end()) return SPhi[n];
int res=0;ll l=2,r=0;
for(;l<=n;l=r+1)
r=n/(n/l),res=(res+1ll*Seive(n/l)*((__(r)-__(l-1)+p)%p)%p)%p;
res=(___(n)-res+p)%p;
return SPhi[n]=res;
}

inline void Work(){
read(p),read(n),Euler();
int res=0;
ll l=1,r=0;
for(;l<=n;l=r+1)
r=n/(n/l),res=(res+1ll*___(n/l)*((Seive(r)-Seive(l-1)+p)%p)%p)%p;
printf("%d\n",res);
}

int main(){Work();}
Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×