「51nod1227」平均最小公倍数 题解
本文网址:https://www.cnblogs.com/zsc985246/p/17392004.html ,转载请注明出处。
题目大意
\[A(n)=\frac{1}{n} \sum_{i=1}^{n} \text{lcm}(n,i)\\
F(a,b)=\sum_{i=a}^{b} A(i)\\
\]
给定 \(a,b\),求 \(F(a,b) \bmod 10^9+7\)。
\(1 \le a \le b \le 10^9\)。
思路
首先我们可以想到,如果我们定义 \(B(n)=\underset{i=1}{\overset{n}{\sum}} A(i)\),那么 \(F(a,b)=B(b)-B(a-1)\)。
然后我们开始逐层推式子。
\[A(n)=\frac{1}{n} \sum_{i=1}^{n} \text{lcm}(n,i)\\
\ \ \ \ \ \ \ \ \ =\frac{1}{n} \sum_{i=1}^{n} \frac{n \times i}{\gcd(n,i)}\\
\ \ \ \ =\sum_{i=1}^{n} \frac{i}{\gcd(n,i)}
\]
然后代入 \(B(n)\) 中,根据套路枚举因数,将分母上的 \(\gcd\) 拿出来:
\[B(n)=\sum_{i=1}^{n} A(i)\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \\
=\sum_{i=1}^{n} \sum_{j=1}^{i} \frac{j}{\gcd(i,j)}\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \\
\ =\sum_{i=1}^{n} \sum_{d|i} \sum_{j=1}^{i} \frac{j}{d}[\gcd(i,j)=d]\\
\ \ \ \ \ =\sum_{i=1}^{n} \sum_{d|i} \sum_{j=1}^{i} \frac{j}{d}[\gcd(\frac{i}{d},\frac{j}{d})=1]\\
\ =\sum_{i=1}^{n} \sum_{d|i} \sum_{j=1}^{\frac{i}{d}} j[\gcd(\frac{i}{d},j)=1]\\
\ \ \ \ \ \ \ \ =\sum_{d=1}^{n} \sum_{i=1}^{n} [d|i]\sum_{j=1}^{\frac{i}{d}} j[\gcd(\frac{i}{d},j)=1]\\
=\sum_{d=1}^{n} \sum_{i=1}^{\lfloor\frac{n}{d}\rfloor} \sum_{j=1}^{i} j[\gcd(i,j)=1]\\
\]
然后我们再用莫比乌斯反演把 \(\gcd\) 打开:
\[\sum_{j=1}^{i} j[\gcd(i,j)=1]=\sum_{j=1}^{i} j \sum_{x|\gcd(i,j)} \mu(x)\\
\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ =\sum_{x|i} \mu(x) \sum_{j=1}^{i} j[j \bmod x = 0]\\
\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ =\sum_{x|i} \mu(x) \sum_{j=1}^{\frac{i}{x}} xj\\
\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ =\sum_{x|i} \mu(x) \frac{1}{2}x(1+\frac{i}{x})\frac{i}{x}\\
\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ =\frac{i}{2} \sum_{x|i} \mu(x) (1+\frac{i}{x})\\
\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ =\frac{i}{2} (\sum_{x|i} \mu(x) + \sum_{x|i} \frac{i}{x}\mu(x))\\
\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ =\frac{i}{2} ([i=1]+ \varphi(i))\\
\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ =\frac{1}{2} ([i=1]+i\varphi(i))
\]
然后我们把这一坨放回原来的式子:
\[B(n)=\sum_{d=1}^{n} \sum_{i=1}^{\lfloor\frac{n}{d}\rfloor} \sum_{j=1}^{i} j[\gcd(i,j)=1]\\
\ \ \ \ \ =\frac{1}{2} \sum_{d=1}^{n} \sum_{i=1}^{\lfloor\frac{n}{d}\rfloor} [i=1]+i\varphi(i)\\
=\frac{n}{2} + \frac{1}{2} \sum_{d=1}^{n} \sum_{i=1}^{\lfloor\frac{n}{d}\rfloor} i\varphi(i)\\
\]
然后我们设 \(f(i)=i\varphi(i)\),我们可以简单地推导出 \(f\) 是一个积性函数。
对于互质的两个数 \(a,b\),\(f(a)f(b)=ab\varphi(a)\varphi(b)\)。
因为 \(\varphi(n)\) 是积性函数,所以 \(\varphi(a)\varphi(b)=\varphi(ab)\)。
所以 \(f(a)f(b)=ab\varphi(ab)=f(ab)\)。
后面是一个整除分块,所以现在我们需要求的实际上是积性函数 \(f(n)\) 的前缀和,可以想到使用杜教筛:
\[(\text{Id} * f)(i)=\sum_{d|i}f(d) \times \text{Id}(\frac{i}{d})\\
\ \ \ \ \ \ \ \ \ \ \ \ \ =\sum_{d|i}d\varphi(d) \times \frac{i}{d}\\
\ \ \ \ =i\sum_{d|i}\varphi(d)\\
=i^2\ \ \ \ \ \ \ \
\]
代入杜教筛公式中,得到:
\[\text{Id}(1)S(n)=\sum_{i=1}^{n} (\text{Id} * f)(i) - \sum_{i=2}^{n} \text{Id}(i)S(\lfloor\frac{n}{i}\rfloor)\\
S(n)=\sum_{i=1}^{n} i^2 - \sum_{i=2}^{n}iS(\lfloor\frac{n}{i}\rfloor)
\]
这样我们就求得了 \(S(n)\)。
最后我们利用推出来的公式求解 \(B(n)\) 就好了。
代码实现
求解 \(B(n)\) 的时候不要忘记除以 \(2\)。
注意取模。
#include<bits/stdc++.h>
#define ll long long
#define For(i,a,b) for(ll i=(a);i<=(b);++i)
#define Rep(i,a,b) for(ll i=(a);i>=(b);--i)
const ll N=1e6+10;
using namespace std;
const ll mod=1e9+7;
ll phi[N],tot,p[N],vis[N];
void init(ll n){//筛出欧拉函数,然后计算f函数前缀和的前n项
phi[1]=1;
For(i,2,n){
if(!vis[i])p[++tot]=i,phi[i]=i-1;
For(j,1,tot){
if(i*p[j]>n)break;
vis[i*p[j]]=1;
if(i%p[j]==0){
phi[i*p[j]]=phi[i]*p[j];
break;
}
phi[i*p[j]]=phi[i]*(p[j]-1);
}
}
For(i,2,n)phi[i]=(phi[i-1]+phi[i]*i%mod)%mod;//计算f函数前缀和
}
map<ll,ll>book;
ll calc(ll n){//求f的前缀和
if(n<=1e6)return phi[n];//提前计算好的
if(book[n])return book[n];//记忆化
//杜教筛
ll s=n*(2*n+1)%mod*(n+1)%mod*166666668%mod,k;//166666668是6在模1000000007下的逆元
for(ll i=2;i<=n;i=k+1){//整除分块
k=n/(n/i);
s=(s-calc(n/i)*(i+k)%mod*(k-i+1)%mod*500000004%mod)%mod;//500000004是2在模1000000007下的逆元
}
return book[n]=s;
}
ll solve(ll n){
ll res=n,k;
for(ll i=1;i<=n;i=k+1){//整除分块
k=n/(n/i);
res=(res+calc(n/i)*(k-i+1)%mod)%mod;
}
return res*500000004%mod;//不要忘了除以2
}
void mian(){
init(1e6);
ll a,b;
scanf("%lld%lld",&a,&b);
printf("%lld",(solve(b)-solve(a-1)+mod)%mod);
}
int main(){
int T=1;
// scanf("%d",&T);
while(T--)mian();
return 0;
}
尾声
如果你发现了问题,你可以直接回复这篇题解
如果你有更好的想法,也可以直接回复!