題意:
求i=1∑nj=1∑nlcm(i,j) mod 109+7n<=1010
題目分析:
化簡,i=1∑nj=1∑nlcm(i,j)=i=1∑nj=1∑ngcd(i,j)i∗j
枚舉gcd=d=1∑nd∗i=1∑dnj=1∑dn[gcd(i,j)==1]∗i∗j
OK,且慢,這裏的gcd(i,j)==1就很講究了,有兩種方法
法一:歐拉函數 φ
因爲∑i=1dn∑j=1dn是對稱的,所以可以化爲
=d=1∑nd∗⎩⎨⎧⎝⎛2i=1∑dnij=1∑ij[(i,j)==1]⎠⎞−1⎭⎬⎫=d=1∑nd∗⎩⎨⎧⎝⎛2i=1∑dni∗2i∗φ(i)+[i=1]⎠⎞−1⎭⎬⎫=d=1∑nd∗i=1∑dni2∗φ(i)
然後按照d分塊優化,要求i2∗φ(i)的前綴和,把它看做id⋅id⋅φ
那麼(id⋅id⋅φ)∗(id⋅id)=
i∣n∑i2∗φ(i)∗(in)2=n2i∣n∑φ(i)=n3=id3
id2和id3的前綴和都很好求,所以問題就解決了
i=1∑ni3=i=1∑nd∣i∑d2∗φ(d)∗(di)2=i=1∑ni2d=1∑ind2∗φ(d)=i=1∑ni2∗F(in)
把i=1提出來,那麼F(n)=i=1∑ni3−i=2∑ni2∗F(in)
i2∗φ(i)是積性函數,可以線性篩一部分,然後分塊優化遞歸求解即可
法一的核心點:i=1∑ni∗[(n,i)==1]=2n∗φ(n)+[n=1]
因爲倘若i與n互質,則n-i也與n互質,這樣的對數有φ(n)/2對,每對的和都爲n
法二:莫比烏斯反演 μ
d=1∑nd∗i=1∑dnj=1∑dn[gcd(i,j)==1]∗i∗j=d=1∑nd∗i=1∑dnj=1∑dni∗j∗k∣(i,j)∑μ(k)=d=1∑nd∗k=1∑dnμ(k)k2i=1∑dknj=1∑dkni∗j
令dk=T
=T=1∑n(2Tn∗(Tn+1))2∗k∣T∑μ(k)∗k2∗kT
按照T分塊優化,要求∑k∣Tμ(k)∗k2∗kT的前綴和(這時候最好不要把T提出去),把它看做(id⋅id⋅μ)∗id
那麼(id⋅id⋅μ)∗id∗(id⋅id)=
(id⋅id⋅μ)∗(id⋅id)∗id=⎝⎛d∣T∑d2∗μ(d)∗(dT)2⎠⎞∗id=⎝⎛T2d∣T∑μ(d)⎠⎞∗id=T∣N∑T2∗(TN)d∣T∑μ(d)=NT∣N∑T[T==1]=id
id2和id的前綴和都很好求,剩下的部分就和法一的轉化方法類似了
法二的核心:[n==1]=d∣n∑μ(d)
兩種方法的共同點:
都利用了卷積(遞歸方法)來求解n比較大時,通常n<=1e9,1e10,1e11,的前綴和問題,構造出f∗g=h的形式,當g,h的前綴和可以O(1)求解,且f爲積性函數,可以線性篩一部分時,求解f的前綴和的時間複雜度爲O(n32)
Ps:兩種方法都試圖通過卷積消去φ或μ前面的係數,從而簡化形式,這是個初學時很難想到的點(至少我沒想到。。。)。
此處貼出法一的代碼,法二請讀者自行嘗試。。。
#include<cstdio>
#include<map>
#define LL long long
using namespace std;
const int mod = 1e9+7, N = 5000000, inv2 = 5e8+4, inv6 = 166666668;
int p[N/10],sum[N+5];
bool v[N+5];
map<LL,int>F;
inline int sqr(int x){return 1ll*x*x%mod;}
void Prime()
{
sum[1]=1;int cnt=0;
for(int i=2;i<=N;i++)
{
if(!v[i]) p[++cnt]=i,sum[i]=1ll*i*i%mod*(i-1)%mod;
for(int j=1,k;j<=cnt&&p[j]*i<=N;j++)
{
v[k=p[j]*i]=1;
if(i%p[j]==0) {sum[k]=1ll*sum[i]*p[j]%mod*sqr(p[j])%mod;break;}
sum[k]=1ll*sum[i]*sum[p[j]]%mod;
}
}
for(int i=2;i<=N;i++) sum[i]=(sum[i]+sum[i-1])%mod;
}
inline LL calc(LL n){return n*(n+1)%mod*(2*n+1)%mod*inv6%mod;}
int solve(LL n)
{
if(n<=N) return sum[n];
if(F.count(n)) return F[n];
int ret=sqr(n%mod*((n+1)%mod)%mod*inv2%mod);
for(LL i=2,j;i<=n;i=j+1)
{
j=n/(n/i);
ret=(ret-(calc(j%mod)-calc((i-1)%mod))*solve(n/i))%mod;
}
return F[n]=ret;
}
int main()
{
Prime();
LL n,ans=0;
scanf("%lld",&n);
for(LL i=1,j;i<=n;i=j+1)
{
j=n/(n/i);
ans=(ans+(i+j)%mod*(j-i+1)%mod*inv2%mod*solve(n/i))%mod;
}
printf("%lld",(ans+mod)%mod);
}