我又来推式子了(找虐了),假设n<=m
这个gcd作分母看起来有点麻烦,我们去掉它:
与其枚举两个数看他们的gcd是否等于d,不如直接枚举d的倍数:
把d提前:
代换:
再次切换枚举,从枚举因数到枚举倍数:
化简:
接下来我们打表u(x)x^2和等差数列求和,只用枚举d,然后用整除分块求后面的式子就行了。//后面还可以优化哦
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=1e7+5,mod=20101009;
int vis[maxn],pri[maxn],mu[maxn],cnt;
int g[maxn],sum[maxn];
void get_mu(int n)
{
mu[1]=1;
for(int i=2;i<=n;i++)
{
if(!vis[i])
pri[++cnt]=i,mu[i]=-1;
for(int j=1;j<=cnt&&pri[j]*i<=n;j++)
{
vis[i*pri[j]]=1;
if(i%pri[j]==0)break;
else mu[i*pri[j]]=-mu[i];
}
}
for(int i=1;i<=n;i++)
{
sum[i]=(sum[i-1]+i)%mod;
g[i]=(g[i-1]+1ll*mu[i]*i*i%mod)%mod;
}
}
int main()
{
int n,m;
ll ans=0;
get_mu(1e7);
scanf("%d%d",&n,&m);
if(n>m)swap(n,m);
for(int i=1;i<=n;i++)
{
int nn=n/i,mm=m/i;
for(int l=1,r;l<=nn;l=r+1)
{
r=min(nn/(nn/l),mm/(mm/l));
ans+=1ll*i*(g[r]-g[l-1]+mod)%mod*sum[nn/l]%mod*sum[mm/r]%mod;
}
}
printf("%lld\n",ans%mod);
}
其实我们可以把第一层for循环枚举也来整除分块,这样更快哦。
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=1e7+5,mod=20101009;
int vis[maxn],pri[maxn],mu[maxn],cnt;
int g[maxn],sum[maxn];
void get_mu(int n)
{
mu[1]=1;
for(int i=2;i<=n;i++)
{
if(!vis[i])
pri[++cnt]=i,mu[i]=-1;
for(int j=1;j<=cnt&&pri[j]*i<=n;j++)
{
vis[i*pri[j]]=1;
if(i%pri[j]==0)break;
else mu[i*pri[j]]=-mu[i];
}
}
for(int i=1;i<=n;i++)
{
sum[i]=(sum[i-1]+i)%mod;
g[i]=(g[i-1]+1ll*mu[i]*i*i%mod)%mod;
}
}
ll work(int n,int m)
{
ll ans=0;
for(int l=1,r;l<=n;l=r+1)
{
r=min(n/(n/l),m/(m/l));
ans+=1ll*(g[r]-g[l-1]+mod)%mod*sum[n/l]%mod*sum[m/r]%mod;
}
return ans;
}
ll cal(int n)
{
return 1ll*n*(n+1)/2%mod;
}
int main()
{
int n,m;
ll ans=0;
get_mu(1e7);
scanf("%d%d",&n,&m);
if(n>m)swap(n,m);
for(int l=1,r;l<=n;l=r+1)
{
r=min(n/(n/l),m/(m/l));
ans+=(cal(r)-cal(l-1)+mod)*work(n/l,m/l)%mod;
}
printf("%lld\n",ans%mod);
}