题目描述
题解
做约数个数和的时候有一个结论:
d(nm)=∑i|n∑j|m[(i,j)=1]
直接套进去
∑i=1n∑j=1m∑x|i∑y|j[(x,y)=1]
然后根据反演公式 [n=1]=∑d|nμ(d)
=∑d=1nμ(d)∑x=1n[d|x]∑i=1n[x|i]∑y=1n[d|y]∑j=1n[y|j]
令 x=dx,y=dy ,再令 i=di,j=dj
=∑d=1nμ(d)(∑x=1nd∑i=1nd[x|i])2
=∑d=1nμ(d)(∑x=1nd⌊ndx⌋)2
令 f(n)=∑i=1n⌊ni⌋ ,这个可以在 O(n√) 的时间内算出
那么原式
=∑d=1nμ(d)f(nd)
不加求 μ 时间是 O(n34) ?不大会算…写出来跑了跑感觉稳
用杜教筛预处理 μ 然后就可以做了
首先线筛出来 n34 的 μ ,然后如果 ni 在 n34 之内就可以直接查询,但是如果不在范围内就需要查询一下了。不过可以发现不在范围内的数非常有限,找出这些数了之后从小到大求,记录一个数组sum(d)表示 [1...nd] 的 μ 的和,这样就可以做到 O(1) 查询了
时间似乎还是 O(n34) ?
代码
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<map>
using namespace std;
#define Mod 1000000007
#define LL long long
#define N 10000005
int n,k=1000000;
int prime[N],p[N],mu[N];
LL ans,sum[N];
void get()
{
mu[1]=1;
for (int i=2;i<=k;++i)
{
if (!p[i])
{
prime[++prime[0]]=i;
mu[i]=-1;
}
for (int j=1;j<=prime[0]&&i*prime[j]<=k;++j)
{
p[i*prime[j]]=1;
if (i%prime[j]==0)
{
mu[i*prime[j]]=0;
break;
}
else mu[i*prime[j]]=-mu[i];
}
}
for (int i=1;i<=k;++i) mu[i]+=mu[i-1];
}
LL getmu(int x)
{
if (x<=k) return mu[x];
return sum[n/x];
}
void summu()
{
int t;
for (t=1;n/t>k;++t);
for (;t;--t)
{
int m=n/t;
sum[t]=1;
for (int i=2,j=0;i<=m;i=j+1)
{
j=m/(m/i);
sum[t]-=getmu(m/i)*(LL)(j-i+1);
sum[t]%=Mod;
}
}
}
LL f(int n)
{
LL ans=0;
for (int i=1,j=0;i<=n;i=j+1)
{
j=n/(n/i);
ans+=(LL)(j-i+1)*(n/i)%Mod;
ans%=Mod;
}
return ans*ans%Mod;
}
int main()
{
scanf("%d",&n);
get();summu();
for (int i=1,j=0;i<=n;i=j+1)
{
j=n/(n/i);
ans+=f(n/i)*(getmu(j)-getmu(i-1))%Mod;
ans%=Mod;
}
ans=(ans+Mod)%Mod;
printf("%lld\n",ans);
}