题意:根据式子
求
想出这题的做法后看了几个博客想验证一下,然而看了四五篇博客发现博主的做法都和我不一样...用自己的想法AC后,发现时间比大部分代码快,直接到rank2,然后略微优化了一下线性筛预处理部分(理论上预处理到n的2/3次方也就是1e6最优,不过这题似乎2e6最优。)
这是我AC后找的某一个博主写的题解,里面的第一种做法和我的比较接近,第二种则是绝大部分人的做法:博文链接
可能还是有人和我一样做的,但是懒得去一个个翻博客了= =
下面开始说我的做法吧题目给出的式子很容易想到莫比乌斯反演,令,则有,则由莫比乌斯反演有。
因此所求式子可转化为,再将这个式子转化一下,可变为,这样后面部分其实就是莫比乌斯函数的前缀和,可以通过杜教筛求得,然后由数论分块可知,出现的莫比乌斯函数前缀和只有O(sqrt(n))个值,而前面部分就只需要每次求一块的和,和后面部分乘起来再累加即可。
(虽然写过两道杜教筛的题...但是并没有自己算过它的复杂度,所以这里的复杂度我只知道杜教筛O(),数论分块O(),但是不会算。。。会算的盆友可以在评论里教教我orz(一些直接说复杂度是O()的博文只说了分块的复杂度啊= =))
代码如下:
(暗戳戳秀一波)
#include<bits/stdc++.h>
using namespace std;
#define For(i,a,b) for(int i=a;i<=b;i++)
#define ll long long
const int maxn=2000000;
const int mod=1e9+7;
int prime[150000],num,miu[maxn+5];
bool vst[maxn+5];
inline ll qpow(ll a,ll b){
ll res=1;
while(b){
if(b&1) res=res*a%mod;
b>>=1;
a=a*1ll*a%mod;
}
return res;
}
inline void Pre(){
miu[1]=1;
for (int i=2;i<=maxn;i++){
if (!vst[i]) prime[++num]=i,miu[i]=-1;
for (int j=1;j<=num && (ll)i*prime[j]<=maxn;j++){
vst[i*prime[j]]=1;
if (i%prime[j]==0){
miu[i*prime[j]]=0;
break;
}
miu[i*prime[j]]=miu[i]*miu[prime[j]];
}
}
for (int i=1;i<=maxn;i++) miu[i]+=miu[i-1];
}
unordered_map<ll,int> S;
inline int Sum(ll n){
if (n<=maxn) return miu[n];
if (S.find(n)!=S.end()) return S[n];
int tem=1; ll l,r;
for (l=2;l*l<=n;l++) tem-=Sum(n/l);
for (ll tt=n/l;l<=n;l=r+1,tt--){
r=n/tt;
tem-=(r-l+1)*Sum(tt);
}
return S[n]=tem;
}
int inv3;
inline int solve(ll n){
ll l,r,tmp=0;
for(l=1;l*l<=n;l++){
if(l<=2)continue;
tmp=(tmp+(l*l-3*l+2)*Sum(n/l))%mod;
}
for(ll tt=n/l;l<=n;l=r+1,tt--){
r=n/tt;
tmp=(tmp+((r*(r-1)%mod*(r-2)%mod*inv3)%mod-((l-1)*(l-2)%mod*(l-3)%mod*inv3)%mod)%mod*Sum(tt))%mod;
}
return (int)((tmp%mod+mod)%mod);
}
int t,n;
int main(){
Pre();
inv3=qpow(3,mod-2);
scanf("%d",&t);
while(t--){
scanf("%d",&n);
printf("%d\n",solve(n));
}
return 0;
}