题解
数学公式什么的太难打了。
我们可以发现一个规律,设分母为n,进制为k,当n⊥kn⊥k即gcd(n,k)=1gcd(n,k)=1时,该小数性质满足题目要求(并不会证)
可以得到:∑ni=1∑mj=1[i⊥j][j⊥k]∑i=1n∑j=1m[i⊥j][j⊥k]
i⊥ji⊥j 是保证最简分数,j⊥kj⊥k已说明。
经过一番化简:∑nd=1[d⊥k]μ(d)⌊nd⌋∑⌊md⌋j=1[j⊥k]∑d=1n[d⊥k]μ(d)⌊nd⌋∑j=1⌊md⌋[j⊥k]
前面一坨(∑nd=1[d⊥k]μ(d)∑d=1n[d⊥k]μ(d))可以递归+杜教筛求得,后面一坨(∑⌊md⌋j=1[j⊥k]∑j=1⌊md⌋[j⊥k])由于k只有2000,可以之间算出来。
代码
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<map>
#include<cstring>
#define mkp(x,y) make_pair(x,y)
using namespace std;
typedef long long ll;
const int N=5e6+5,lim=5e6;
int n,m,k,g[2002],mu[N],sum[N],p[N],num,mn;
bool pri[N];
ll ans,cur,past;
map<pair<int,int>,int>mp;
inline int gcd(int x,int y){return !y?x:gcd(y,x%y);}
inline void pre()
{
int i,j,now;
for(i=1;i<=k;++i) g[i]= gcd(i,k)==1? g[i-1]+1:g[i-1];
memset(pri,true,sizeof(pri));mu[1]=1;
for(i=2;i<=lim;++i){
if(pri[i]){mu[i]=-1;p[++num]=i;}
for(j=1;j<=num && 1ll*p[j]*i<=(ll)lim;++j){
pri[(now=p[j]*i)]=false;
if(i%p[j]==0){mu[now]=0;break;}
mu[now]=-mu[i];
}
}
for(i=1;i<=lim;++i) sum[i]=sum[i-1]+mu[i];
}
inline int G(int x){return (x/k)*g[k]+g[x%k];}
inline int F(int x,int k)
{
if((k==1 && x<=lim)||(!x)) return sum[x];
if(mp[mkp(x,k)]) return mp[mkp(x,k)];
if(k==1){
int ret=1;
for(int i=2,j;i<=x;i=j+1){
j=x/(x/i);
ret-=(j-i+1)*F(x/i,1);
}
mp[mkp(x,k)]=ret;
return ret;
}else{
int ret=0;
for(int i=1;i*i<=k;++i){
if(k%i==0){
if(mu[i]) ret+=F(x/i,i);
if(i*i!=k && mu[k/i]) ret+=F(x/(k/i),k/i);
}
}
mp[mkp(x,k)]=ret;
return ret;
}
}
int main(){
int i,j;
scanf("%d%d%d",&n,&m,&k);
pre();mn=min(n,m);
for(i=1;i<=mn;i=j+1){
j=min(n/(n/i),m/(m/i));
cur=F(j,k);
ans+=1ll*n/i*(cur-past)*G(m/i);
past=cur;
}
printf("%lld\n",ans);
}