链接:https://ac.nowcoder.com/acm/contest/549/J
来源:牛客网
时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 32768K,其他语言65536K
64bit IO Format: %lld
题目描述
小A最近开始研究数论题了,这一次他随手写出来一个式子,∑ni=1∑mj=1gcd(i,j)2∑i=1n∑j=1mgcd(i,j)2,但是他发现他并不太会计算这个式子,你可以告诉他这个结果吗,答案可能会比较大,请模上1000000007。
输入描述:
一行两个正整数n,m一行两个正整数n,m
输出描述:
一行一个整数表示输出结果一行一个整数表示输出结果
示例1
输入
2 2
输出
7
备注:
1≤n,m≤1e6
计算这个式子的时候,我一开始就想到了计算每个gcd的贡献。
例如,假设存在 i 和 j 的gcd为 d 那么就去计算d 出现的次数。
首先来看gcd 有多少种可能,很容易得出,t=min(n,m) 那么1~t都是有可能出现的。
剩下的就是对于每个 gcd 来计数。
例如,计算gcd 为 3 的贡献。那么 3 出现的次数为 (n/3)*(m/3)。既代表 i 为3的倍数出现的次数乘以 j 为 3 的倍数的次数。
不过这里的计算会有重复,例如,gcd 为 6 的情况会被一起算入,那么这里就需要用到一部分减法原则。也就是比较简单的容斥原理。
当我们用 (n/3)*(m/3)的方式计算 gcd 为 3 的贡献时,需要去除掉当中所有 gcd为3的倍数的种类数。例如 6、9、12。
所以可以获得下面的公式。
d=gcd(i,j) 那么d出现的次数(贡献) f(d)=(n/d)*(m/d)-f(2*d)-f(3*d)-........
下面是代码:
#include<iostream>
using namespace std;
typedef long long ll;
const ll mod=1000000007;
ll f[1000005]={0};
int main(){
ll n,m,i,j;
cin>>n>>m;
ll ans=n>m?m:n,sum; //ans取n和m当中小的那一个
sum=0;
for(i=ans;i>=1;i--){ // 对于每一个 gcd=i的贡献(出现的次数)计算
f[i]=(ll)(n/i)*(ll)(m/i);
for(j=i+i;j<=ans;j=j+i)
f[i]=f[i]-f[j];
sum=(sum+f[i]*(i*i%mod)%mod)%mod; //疯狂取模防止爆 long long
}
cout<<sum<<endl;
return 0;
}