Loj #2085. 「NOI2016」循環之美

Loj #2085. 「NOI2016」循環之美

題目描述

牛牛是一個熱愛算法設計的高中生。在他設計的算法中,經常會使用帶小數的數進行計算。牛牛認爲,若是在 \(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;
}
相關文章
相關標籤/搜索