(数据范围 n<=10^9 ,T<=10 )
首先,我来证明一下 Σμ(d) * σ(i/d)^2 = σ(i^2)
相信做过约数个数和的童鞋都可以完成从右式推到左式,那么我现在就说一下怎么从左边推到右边。
Σμ(d) * σ(i/d)^2
= Σμ(d) * Σ(d|p|i) * Σ(d|q|i)
= Σ(p|i) * Σ(q|i) * Σ(d|p,d|q) μ(d)
= Σ(p|i) * Σ(q|i) * [gcd(p,q)==1]
= σ(i^2)
然后这个题就变成了昨天晚上我做的那个题。
但是我突然又想再推一遍式子。。。
σ(i^2)
= (2*a1 + 1)(2*a2 + 1)(2*a3 + 1)...(2*ak + 1)
= Σ(S属于{1,2,...k}) 2^|S| * π(i属于S) ai
= Σ (p|i) 2^w(p)
其中w(x)表示x的质因子个数。
又因为 2^w(x) = Σ (d|x) μ(d)^2
所以上式的前缀和就可以转化成μ^2和σ的前缀和之间的运算了
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=1000000;
int zs[maxn/5],t=0,T,sq[maxn+5];
int miu[maxn+5],low[maxn+5],n;
bool v[maxn+5];
ll d[maxn+5];
inline void init(){
miu[1]=1,d[1]=1,low[1]=1;
for(int i=2;i<=maxn;i++){
if(!v[i]) zs[++t]=i,miu[i]=-1,d[i]=2,low[i]=i;
for(int j=1,u;j<=t&&(u=zs[j]*i)<=maxn;j++){
v[u]=1;
if(!(i%zs[j])){
low[u]=low[i]*zs[j];
if(low[i]==i) d[u]=d[i]+1;
else d[u]=d[low[u]]*d[i/low[i]];
break;
}
low[u]=zs[j];
d[u]=d[i]<<1;
miu[u]=-miu[i];
}
}
for(int i=1;i<=maxn;i++) d[i]+=d[i-1];
for(int i=1;i<=maxn;i++) sq[i]=sq[i-1]+miu[i]*miu[i];
}
inline int getsq(int x){
if(x<=maxn) return sq[x];
ll an=0;
for(int i=1;i*(ll)i<=x;i++){
an+=miu[i]*(x/(i*(ll)i));
}
return an;
}
inline ll getd(int x){
if(x<=maxn) return d[x];
ll an=0;
for(int i=1,j,now;i<=x;i=j+1){
now=x/i,j=x/now;
an+=(j-i+1)*(ll)now;
}
return an;
}
inline ll query(int x){
ll an=0;
for(int i=1,j,now;i<=x;i=j+1){
now=x/i,j=x/now;
an+=(getsq(j)-getsq(i-1))*getd(now);
}
return an;
}
int main(){
freopen("function.in","r",stdin);
freopen("function.out","w",stdout);
scanf("%d",&T);
init();
while(T--){
scanf("%d",&n);
printf("%lld\n",query(n));
}
return 0;
}