为么很多这种题把
∑
顺序换一下就得到答案了。。。
一个植物(坐标(x,y))到原点的路线上经过的植物数是gcd(x,y)(包括那个植物本身)
∑((gcd−1)∗2+1)=∑(gcd∗2−1)
因此把
gcd∗2−1
的和求出来即可。
下面说一说如何快速求
∑ai=1∑bj=1gcd(i,j)
.
假设a不大于b。
∑ai=1∑bj=1gcd(i,j)
由
∑d|nϕ(d)=n
得
=∑ai=1∑bj=1∑d|gcd(i,j)ϕ(d)
=∑ad=1∑⌊a/d⌋i=1∑⌊b/d⌋j=11
=∑ad=1⌊ad⌋⌊bd⌋
然后求出phi的前缀和用那种神奇的根号扫描法(详见bzoj1101)就可以在
O(a√+b√)
的时间内解决询问了。
#include<cstring>
#include<iostream>
#include<cstdio>
using namespace std;
const int maxn=100001;
int prime[maxn/3],phi[maxn],tot;
bool isprime[maxn];
void genPrime(int n){
memset(isprime,true,sizeof isprime);
phi[1]=1;
for(int i=2;i<=n;++i){
if(isprime[i]){
prime[tot++]=i;
phi[i]=i-1;
}
for(int j=0;j<tot&&i*prime[j]<=n;++j){
isprime[i*prime[j]]=false;
if(i%prime[j]==0){
phi[i*prime[j]]=phi[i]*prime[j];
break;
}
else phi[i*prime[j]]=phi[i]*(prime[j]-1);
}
}
}
int main(){
int n,m;
scanf("%d%d",&n,&m);
if(n>m) swap(n,m);
genPrime(m);
for(int i=1;i<=n;++i) phi[i]+=phi[i-1];
long long ans=0;
for(int i=1,nex;i<=n;i=nex+1){
nex=min(n/(n/i),m/(m/i));
ans+=1LL*(phi[nex]-phi[i-1])*(n/i)*(m/i);
}
cout<<ans*2-(long long)n*m<<"\n";
return 0;
}