题解
数论劝退…真的对第一次学的我太不友好了…
请务必看一下大佬的博客
首先,看到 要能想到莫比乌斯反演,
在莫比乌斯反演里有这么一个定理:
如果 F(n) 和 f(n) 满足
,
那么
,
即
,
其中
是莫比乌斯函数,是个积性函数哦
是不是和题目中的 联系起来了?
顺便改写一下题目,
我们知道杜教筛是用来求积性函数的前缀和的,
那就直接让
吧!
然后,我们套用杜教筛常用公式
,
这里的
选用恒等函数
,对就是那个值恒等于1的积性函数,
然后改写一下公式:
,
注意区分一下 ·
是数值上的乘法,*
是卷积,之前就这里把自己绕晕了
还记得之前说的
吗?
再改一下,
学习大佬博客后知道
,那个表示单位的元函数
(也就是说
是
的逆元)
最后式子变成,
结合题目,
其中
最终,
我终于会了!!!
#include <bits/stdc++.h>
#include <tr1/unordered_map>//unordered_map所在的头文件
using namespace std;
typedef long long ll;
const int N=1e6+10;
const int mod=1e9+7;
int m;
unordered_map<int,ll>S;//n高达1e9 需要unordered_map存答案记忆化
ll inv3;
ll F(int n){return 1ll*n*n-1ll*3*n+2;}
ll qpow(ll a,ll b){
ll res=1;
while(b){
if(b&1)res=res*a%mod;
b>>=1;
a=a*a%mod;
}
return res;
}
ll sum_f[N];
ll get_S(int n){
if(n<=m) return sum_f[n];//如果n的范围小于根号(最大范围) 直接返回提前预处理好的前缀和
if(S[n]) return S[n];//记忆化
ll res=1ll*n*(n-1)%mod*(n-2)%mod*inv3%mod;//F前缀和
for (int l = 2,r; l <=n ; l=r+1) {
r=n/(n/l);
res-=(r-l+1)*get_S(n/l)%mod;
//I(d)在[l,r]的和
res=(res+mod)%mod;//有负数的风险
}
return S[n]=res;
}
int n,k;
int mu[N],prime[N],tot=0;
bool vis[N];
void get_mu(){//筛选莫比乌斯函数
mu[1]=1;
for (int i = 2; i < N; ++i) {
if(!vis[i]){
prime[++tot]=i;
mu[i]=-1;
}
for (int j = 1; j <= tot && i*prime[j]<N; ++j) {
vis[i*prime[j]]=true;
if(i%prime[j]==0){
mu[i*prime[j]]=0;//里面肯定有两个prime[j]
break;
}else{
mu[i*prime[j]]=-mu[i];//多了个质数
}
}
}
}
void init(){
m=(int)sqrt(1e9);//sqrt(1e9)
inv3=qpow(3,mod-2);
get_mu();
for (int i = 1; i <= m; ++i) {//统计每个f[n] 就是反演那个推论
for (int j = i; j <= m; j+=i) {
sum_f[j]+=1ll*mu[i]*F(j/i);
sum_f[j]%=mod;
}
}
for (int i = 1; i <= m; ++i) {//小范围统计S[n]
sum_f[i]=sum_f[i]+sum_f[i-1];
sum_f[i]%=mod;
}
}
int main(){
ios::sync_with_stdio(0);
init();//不预处理必T
int T;
cin>>T;
for (int cs = 1; cs <= T; ++cs) {
cin>>n;
cout <<get_S(n) << endl;
}
return 0;
}