【题解】LOJ #2085 / 洛谷 P1587「NOI2016」循环之美【莫比乌斯反演】
题意
问 \(k\) 进制下,分子为 \([1,n]\),分母为 \([1,m]\) 的纯循环小数有多少个(值相同算同一个)。\(n,m\leq 10^9\),\(k\leq 2000\)。
题解
根据我们小学二年级就学过的结论,对于 \(k\) 进制下的最简分数 \(\dfrac{x}{y}\) 是纯循环小数,当且仅当 \(y\bot k\)。于是问题变为:
\[\begin{aligned}
&\sum_{i=1}^{n}\sum_{j=1}^{n}[i\bot j][j\bot k]\\
=&\sum_{j=1}^{m}[j\bot k]\sum_{j=1}^{n}[i\bot j]\\
=&\sum_{j=1}^{m}[j\bot k]\sum_{j=1}^{n}\sum_{d|\gcd(i,j)}\mu d\\
=&\sum_{d=1}^{n}[d\bot k]\mu(d)\sum_{j=1}^{m /d}\sum_{i=1}^{n /d}[j\bot k]\\
=&\sum_{d=1}^{n}[d\bot k]\mu(d)\lfloor \dfrac{n}{d}\rfloor\sum_{j=1}^{m /d}[j\bot k]
\end{aligned}
\]
设
-
\[f(x)=\sum_{i=1}^x[i\bot k] \]
- 由于 \([i\bot k]\) 是 \(f\) 个一循环的,预处理 \(f(1..k)\) 后容易计算。
-
\[g(x,k)=\sum_{i=1}^x[i\bot k]\mu(i) \]
\[\begin{aligned}
g(x,k)=&\sum_{i=1}^x[i\bot k]\mu(i)\\
=&\sum_{i=1}^x\mu(i)\sum_{i|d}\mu(i)\\
=&\sum_{d|k}^{x}\mu(d)\sum_{i|d}\mu(i)\\
=\sum_{d|k}^{x}\mu(d)\sum_{p=1}^{x /d}\mu(pd)
\end{aligned}
\]
当 \(p\not \bot d\),\(\mu(pd)=0\)。我们继续:
\[\begin{aligned}
g(x,k)=&\sum_{d|k}^{x}\mu^2(d)\sum_{p=1}^{x /d}[p\bot d]\\
=&\sum_{d|k}^{x}[mu(d)\neq 0]\sum_{p=1}^{x /d}\mu(p)[p\bot d]\\
=&\sum_{d|k}^{x}[mu(d)\neq 0]g(\lfloor \dfrac{n}{d}\rfloor,d)
\end{aligned}
\]
递归计算,\(g(n,1)\) 为 \(\mu\) 的前缀和。
#include<bits/stdc++.h>
#include<unordered_map>
using namespace std;
int getint(){
int ans=0,f=1;
char c=getchar();
while(c>'9'||c<'0'){
if(c=='-')f=-1;
c=getchar();
}
while(c>='0'&&c<='9'){
ans=ans*10+c-'0';
c=getchar();
}
return ans*f;
}
#define ll long long
const int M=2e6,N=1e9;
int pri[M+1],mu[M+1],cnt=0;
bool vis[M+1],v[M];
unordered_map<int,int> mu2;
int n,m,k;
int f_[M];
int gcd(int x,int y){ return x?gcd(y%x,x):y; }
int f(int x){
return f_[k]*(x/k)+f_[x%k];
}
int get_mu(int x){
//f=mu,g=I,f*g=e
if(x<=M)return mu[x];
if(mu2.count(x))return mu2[x];
ll ans=bool(x);
for(int i=2,j;j<x&&i<=x;i=j+1){
j=x/(x/i);
ans-=(j+1ll-i)*get_mu(x/i);
}
return mu2[x]=ans;
}
ll g(int x,int k){
if(k==1)return get_mu(x);
if(!x)return 0;
ll ans=0;
for(int i=1;i*i<=k;i++){
if(k%i)continue;
if(x>=i&&mu[i]-mu[i-1])ans=ans+g(x/i,i);
if(x>=k/i&&(mu[k/i]-mu[k/i-1])&&i*i!=k)ans=ans+g(x/(k/i),k/i);
}
return ans;
}
int main(){
mu[1]=1;
for(int i=2;i<=M;i++){
if(!vis[i])pri[cnt++]=i,mu[i]=-1;
for(int j=0;j<cnt&&i*pri[j]<=M;j++){
vis[i*pri[j]]=1;
if(i%pri[j])mu[i*pri[j]]=-mu[i];
else break;
}
mu[i]+=mu[i-1];
}
n=getint(),m=getint(),k=getint();
for(int i=1;i<=k;i++)f_[i]=f_[i-1]+(gcd(i,k)==1);
ll ans=0;
for(ll l=1,r,sl=0;l<=min(n,m);l=r+1){
r=min(n/(n/l),m/(m/l));
int p=n/l,q=m/l;
ll sr=g(r,k);
ans+=(sr-sl)*p*f(q);
sl=sr;
}
cout<<ans<<endl;
return 0;
}