题解
首先列出题目要求的式子
ans=∑i=1n∑j=1nd(ij)
一看就不会做,百度一下我就知道有一个结论,于是套上结论。
ans=∑i=1n∑j=1n∑a|i∑b|jajb[(a,b)=1]
你问我为啥?我也不知道
ans=∑i=1n∑j=1n∑a|i∑b|jajb∑d|(a,b)μ(d)=∑d=1nμ(d)∑d|an∑d|bn∑a|in∑b|jnajb=∑d=1nμ(d)∑d|an∑d|bn∑a|in∑b|jnajb=∑d=1nμ(d)∑d|adn∑d|bdn∑ad|in∑bd|jnajb=∑d=1nμ(d)∑a=1⌊nd⌋∑b=1⌊nd⌋∑i=1⌊nad⌋∑j=1⌊nbd⌋ajbdb=∑d=1nμ(d)∑a=1⌊nd⌋∑b=1⌊nd⌋∑i=1⌊nad⌋∑j=1⌊nbd⌋ajd=∑d=1ndμ(d)∑a=1⌊nd⌋a⌊⌊nd⌋a⌋∑b=1⌊nd⌋∑j=1⌊nbd⌋j
最后面那两个∑可以考虑用算贡献的方法化简
∑b=1⌊nd⌋∑j=1⌊⌊nd⌋b⌋j=∑j=1⌊nd⌋j⌊⌊nd⌋j⌋
令D(n)表示约数和函数d()的前n项和。
ans=∑d=1nμ(d)D(⌊nd⌋)2
dμ(d)可以杜教筛,D则可以小数据线性筛+大数据暴力。
时间复杂度
代码
//杜教筛
#include <cstdio>
#include <algorithm>
#include <cmath>
#define maxn 1000005
#define mod 1000000007ll
using namespace std;
typedef long long ll;
ll N, f[maxn+10], d[maxn+10];
int prime[maxn/10+10], mu[maxn+10], pr[maxn+10], prd[maxn+10];
bool mark[maxn+10];
void shai()
{
int i, j;
d[1]=mu[1]=1;
for(i=2;i<maxn;i++)
{
if(!mark[i])prime[++*prime]=i, mu[i]=-1, d[i]=i+1, pr[i]=i, prd[i]=i;
else
if(prd[i]==i)d[i]=((ll)prd[i]*pr[i]-1)/(pr[i]-1);
else d[i]=d[prd[i]]*d[i/prd[i]];
for(j=1;i*prime[j]<maxn;j++)
{
mark[i*prime[j]]=1;
pr[i*prime[j]]=prime[j];
if(i%prime[j]==0){prd[i*prime[j]]=prd[i]*prime[j];break;}
mu[i*prime[j]]=-mu[i];
prd[i*prime[j]]=prime[j];
}
}
for(i=1;i<maxn;i++)mu[i]=(mu[i-1]+(ll)i*mu[i])%mod, d[i]=(d[i-1]+d[i])%mod;
}
ll s1(ll x){return (x*(1+x)>>1)%mod;}
ll getf(ll n){return n<maxn?mu[n]:f[N/n];}
void djs(ll n)
{
if(n<maxn or getf(n))return;
ll i, last, t=N/n;
f[t]=1;
for(i=2;i<=n;i=last+1)
{
last=n/(n/i);
djs(n/i);
f[t]=(f[t]-(s1(last)-s1(i-1))*getf(n/i))%mod;
}
}
ll sqr(ll x){return x*x%mod;}
ll getd(ll n)
{
if(n<maxn)return d[n];
ll ans=0, i, last;
for(i=1;i<=n;i=last+1)
{
last=n/(n/i);
ans=(ans+(s1(last)-s1(i-1))*(n/i))%mod;
}
return ans;
}
int main()
{
ll i, last, ans=0, a, b, c;
shai();
scanf("%lld",&N);
djs(N);
for(i=1;i<=N;i=last+1)
{
last=N/(N/i);
a=getf(last)-getf(i-1);
b=sqr(getd(N/i));
ans=(ans+a*b)%mod;
}
printf("%lld",(ans+mod)%mod);
return 0;
}