题如其名,这道题十分优美
首先,如果$\dfrac xy$在$k$进制下是循环节长度为$l$的纯循环小数,那么$\dfrac xy$和$\dfrac{xk^l}y$的小数部分是一样的,也就是说$x\equiv xk^l(\bmod y)$,即$k^l\equiv1(\bmod y)$,这时就有$(k,y)=1$,显然以上每一步都是充分必要的
所以我们要求$\sum\limits_{i=1}^n\sum\limits_{j=1}^m[(i,j)=1][(j,k)=1]$,先推一下式子
$\begin{aligned}\sum\limits_{i=1}^n\sum\limits_{j=1}^m[(i,j)=1][(j,k)=1]&=\sum\limits_{j=1}^m[(j,k)=1]\sum\limits_{i=1}^n\sum\limits_{\substack{d|i\\d|j}}\mu(d)\\&=\sum\limits_{d=1}^{\min(n,m)}\mu(d)\left\lfloor\frac nd\right\rfloor\sum\limits_{j=1}^{\left\lfloor\frac md\right\rfloor}[(jd,k)=1]\\&=\sum\limits_{d=1}^{\min(n,m)}\mu(d)\left\lfloor\frac nd\right\rfloor[(d,k)=1]\sum\limits_{j=1}^{\left\lfloor\frac md\right\rfloor}[(j,k)=1]\end{aligned}$
现在我们要求两个东西:$f(n)=\sum\limits_{i=1}^n[(i,k)=1]$和$g(n,k)=\sum\limits_{i=1}^n\mu(i)[(i,k)=1]$
$f(n)=\left\lfloor\frac nk\right\rfloor f(k)+f(n\%k)$,预处理$f(1\cdots k)$即可
$\begin{aligned}g(n,k)&=\sum\limits_{i=1}^n\mu(i)[(i,k)=1]\\&=\sum\limits_{i=1}^n\mu(i)\sum\limits_{\substack{d|i\\d|k}}\mu(d)\\&=\sum\limits_{d|k}\mu(d)\sum\limits_{i=1}^{\left\lfloor\frac nd\right\rfloor}\mu(id)\\&=\sum\limits_{d|k}\mu(d)\sum\limits_{i=1}^{\left\lfloor\frac nd\right\rfloor}\mu(id)[(i,d)=1]\\&=\sum\limits_{d|k}\mu^2(d)\sum\limits_{i=1}^{\left\lfloor\frac nd\right\rfloor}\mu(i)[(i,d)=1]\\&=\sum\limits_{d|k}\mu^2(d)g\left(\left\lfloor\frac nd\right\rfloor,d\right)\end{aligned}$
中间多出来的中括号不是无中生有,因为如果$(i,d)\ne1$那么$\mu(id)=0$,这一步是比较妙的地方
于是我们可以递归求值,递归到$k=1$时直接杜教筛,这里的时间复杂度是$O(n^{\frac23})$,观察递归式,我们可以只分析$d$的取值,有$O(\sigma_0(k))$个不同的$d$,每次转移耗费时间为$O(\sigma_0(d))$,加上最外层的根号分段,总时间复杂度为$O\left(n^{\frac23}+\sigma_0^2(k)\sqrt n\right)$,当然这个上界很松,所以跑得还是挺快的
The beautiful math.
#include<stdio.h>
#include<algorithm>
#include<map>
using namespace std;
typedef long long ll;
const int T=1000000;
int pr[T+10],mu[T+10],smu[T+10];
bool np[T+10];
void sieve(){
int i,j,M=0;
mu[1]=1;
for(i=2;i<=T;i++){
if(!np[i]){
pr[++M]=i;
mu[i]=-1;
}
for(j=1;j<=M&&i*pr[j]<=T;j++){
np[i*pr[j]]=1;
if(i%pr[j]==0)break;
mu[i*pr[j]]=-mu[i];
}
}
for(i=1;i<=T;i++)smu[i]=smu[i-1]+mu[i];
}
int gcd(int a,int b){return a%b==0?b:gcd(b,a%b);}
int f[2010];
map<int,int>g[2010];
int get(int n,int k){
if(n==0)return 0;
if(g[k].count(n))return g[k][n];
if(k==1){
if(n<=T)return smu[n];
if(g[1].count(n))return g[1][n];
int i,nex,s;
s=1;
for(i=2;i<=n;i=nex+1){
nex=n/(n/i);
s-=(nex-i+1)*get(n/i,1);
}
return g[1][n]=s;
}
int i,s;
s=0;
for(i=1;i*i<=k;i++){
if(k%i==0){
if(mu[i])s+=get(n/i,i);
if(i*i!=k&&mu[k/i])s+=get(n/(k/i),k/i);
}
}
return g[k][n]=s;
}
int main(){
sieve();
int n,m,k,i,N,nex;
ll s;
scanf("%d%d%d",&n,&m,&k);
for(i=1;i<=k;i++)f[i]=f[i-1]+(gcd(i,k)==1);
N=min(n,m);
s=0;
for(i=1;i<=N;i=nex+1){
nex=min(n/(n/i),m/(m/i));
s+=(n/i)*((m/i/k)*(ll)f[k]+f[m/i%k])*(get(nex,k)-get(i-1,k));
}
printf("%lld",s);
}