牛牛是一个热爱算法设计的高中生。在他设计的算法中,经常会使用带小数的数进行计算。牛牛认为,若是在 \(k\) 进制下,一个数的小数部分是纯循环的,那么它就是美的。c++
如今,牛牛想知道:对于已知的十进制数 \(n\) 和 \(m\),在 \(k\) 进制下,有多少个数值上互不相等的纯循环小数,能够用分数 \(\frac x y\) 表示,其中 \(1\le x\le n,1\le y\le m\),且 \(x,y\) 是整数。算法
一个数是纯循环的,当且仅当其能够写成如下形式:函数
\[a.\dot{c_1} c_2 c_3 \ldots c_{p - 1} \dot{c_p}\]测试
其中,\(a\) 是一个整数,\(p\ge1\);对于 \(1\le i\le p\),\(c_i\) 是 \(k\) 进制下的一位数字。spa
例如,在十进制下,\(0.45454545\dots=0.\dot{4}\dot{5}\) 是纯循环的,它能够用 \(\frac 5 {11}\)、\(\frac{10}{22}\) 等分数表示;在十进制下,\(0.1666666\dots=0.1\dot{6}\) 则不是纯循环的,它能够用 \(\frac 1 6\) 等分数表示。设计
须要特别注意的是,咱们认为一个整数是纯循环的,由于它的小数部分能够表示成 \(0\) 的循环或是 \(k-1\) 的循环;而一个小数部分非 \(0\) 的有限小数不是纯循环的。code
输入文件只有一行,包含三个十进制数 \(n,m,k\),意义如题所述。递归
只输出一行一个整数,表示知足条件的美的数的个数。get
对于全部的测试点,保证 \(1\le n\le 10^9\),\(1\le m\le 10^9\),\(2\le k\le2000\)。it
首先有个结论就是,若是\(\frac{x}{y}\ \gcd(x,y)=1\)是个在\(k\)进制意义下是纯循环小数,那么\(\gcd(y,k)=1\)。
因此:
\[ \begin{align} ans&=\sum_{i=1}^n\sum_{j=1}^m[\gcd(i,j)=1][\gcd(j,k)=1]\\ &=\sum_{i=1}^n\sum_{j=1}^m[\gcd(j,k)=1]\sum_{d|i,d|j}\mu(d) \end{align} \]
由于\(\gcd(j,k)=1,d|j\),因此\(\gcd(d,k)=1\)。
\[ \begin{align} ans&=\sum_{\gcd(d,k)=1}\mu(d)\sum_{i=1}^{\lfloor\frac{n}{d}\rfloor}\sum_{j=1}^{\lfloor\frac{m}{d}\rfloor}[\gcd(j,k)=1]\\ &=\sum_{\gcd(d,k)=1}\mu(d)\lfloor\frac{n}{d}\rfloor \sum_{j=1}^{\lfloor\frac{m}{d}\rfloor}[\gcd(j,k)=1]\\ \end{align} \]
这就是一个整除分块的形式了。可是咱们还要求出一下两个函数才能求出答案。
\[ f(n,k)=\sum_{i=1}^n[\gcd(i,k)=1] \]
\[ S(n,k)=\sum_{i=1}^n[\gcd(i,k)=1]\mu(i) \]
先看\(f\)。由于\(\gcd(i,k)=\gcd(i+k,k)\),因此没\(k\)段中,与\(k\)的互质的数的个数都是相同的。咱们先预处理出\(f(1\ldots k,k)\)的值,那么\(f(n,k)=\lfloor \frac{n}{k}\rfloor f(k,k)+f(n\%k,k)\)。
再来看\(S\)。
\[ S(n,k)=\sum_{i=1}^n[\gcd(i,k)=1]\mu(i)\\ =\sum_{i=1}^n\mu(i)\sum_{d|i,d|k}\mu(d)\\ =\sum_{d|k}\mu(d)\sum_{i=1}^{\lfloor\frac{n}{d}\rfloor}\mu(i*d) \]
又由于,\(\gcd(i,d)!=1\)时,\(\mu(i*d)=0\),因此:
\[ \begin{align} S(n,k)&=\sum_{d|k}\mu(d)\sum_{i=1,\gcd(i,d)=1}^{\lfloor\frac{n}{d}\rfloor}\mu(i*d)\\ &=\sum_{d|k}\mu(d)^2\sum_{i=1}^{\lfloor\frac{n}{d}\rfloor}[\gcd(i,d)=1]\mu(i)\\ &=\sum_{d|k}\mu(d)^2S(\lfloor\frac{n}{d}\rfloor,d) \end{align} \]
因而能够递归求\(S\)。当递归到\(k=1\)时,答案就是\(\mu\)的前缀和,这个用杜教筛就能够了。
代码:
#include<bits/stdc++.h> #define ll long long #define K 2005 #define maxx 1000005 using namespace std; inline int Get() {int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}while('0'<=ch&&ch<='9') {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}return x*f;} int n,m,k; int gcd(int a,int b) {return !b?a:gcd(b,a%b);} ll ans; ll f[K]; int pr[maxx],vis[maxx]; ll u[maxx]; void pre(int n) { u[1]=1; for(int i=2;i<=n;i++) { if(!vis[i]) pr[++pr[0]]=i,u[i]=-1; for(int j=1;j<=pr[0]&&1ll*i*pr[j]<=n;j++) { vis[i*pr[j]]=1; if(i%pr[j]==0) { u[i*pr[j]]=0; break; } u[i*pr[j]]=-u[i]; } } for(int i=1;i<=n;i++) u[i]+=u[i-1]; } map<ll,int>st; ll Sum(ll n) { if(n<=1e6) return u[n]; if(st.find(n)!=st.end()) return st[n]; ll ans=1; ll last=2,now; for(;last<=n;last=now+1) { now=n/(n/last); ans-=(now-last+1)*Sum(n/last); } return st[n]=ans; } ll cal_f(int n) {return n/k*f[k]+f[n%k];} map<ll,ll>S[K]; ll cal_S(int n,int k) { if(!n) return 0; if(S[k].find(n)!=S[k].end()) return S[k][n]; if(k==1) return S[k][n]=Sum(n); ll ans=0; for(int d=1;d<=k;d++) { if(k%d) continue ; ans+=(u[d]-u[d-1])*(u[d]-u[d-1])*cal_S(n/d,d); } return S[k][n]=ans; } int main() { pre(1e6); n=Get(),m=Get(),k=Get(); for(int i=1;i<=k;i++) f[i]=f[i-1]+(gcd(i,k)==1); ll now,last=1; ll ans=0; for(int lim=min(n,m);last<=lim;last=now+1) { now=min(n/(n/last),m/(m/last)); ans+=(cal_S(now,k)-cal_S(last-1,k))*(n/last)*cal_f(m/last); } cout<<ans; return 0; }