1. 程式人生 > >[bzoj4652] [NOI2016]迴圈之美

[bzoj4652] [NOI2016]迴圈之美

Description

牛牛是一個熱愛演算法設計的高中生。在他設計的演算法中,常常會使用帶小數的數進行計算。牛牛認為,如果在 k

進位制下,一個數的小數部分是純迴圈的,那麼它就是美的。現在,牛牛想知道:對於已知的十進位制數 n 和 m,在

kk 進位制下,有多少個數值上互不相等的純迴圈小數,可以用分數 xy 表示,其中 1≤x≤n,1≤y≤m,且 x,y是整數

。一個數是純迴圈的,當且僅當其可以寫成以下形式:a.c1˙c2c3…cp-1cp˙其中,a 是一個整數,p≥1;對於 1

≤i≤p,ci是 kk 進位制下的一位數字。例如,在十進位制下,0.45454545……=0.4˙5˙是純迴圈的,它可以用 5/11

、10/22 等分數表示;在十進位制下,0.1666666……=0.16˙則不是純迴圈的,它可以用 1/6 等分數表示。需要特

別注意的是,我們認為一個整數是純迴圈的,因為它的小數部分可以表示成 0 的迴圈或是 k?1 的迴圈;而一個小

數部分非 0 的有限小數不是純迴圈的。

Input

只有一行,包含三個十進位制數N,M,K意義如題所述,保證 1≤n≤10^9,1≤m≤10^9,2≤k≤2000

Output

一行一個整數,表示滿足條件的美的數的個數。

Sample Input

2 6 10

Sample Output

4

Solution

\(k\)進位制下\(x/y\)為迴圈小數,即:
\[ \big[\frac{xk^l}{y}\big]=\big[\frac{x}{y}\big] \]


其中中括號表示小數部分,\(l\)\(k\)進位制下迴圈節長度。

中括號展開可得:
\[ \frac{xk^l}{y}-\lfloor\frac{xk^l}{y}\rfloor=\frac{x}{y}-\lfloor\frac{x}{y}\rfloor\\ xk^l-y\lfloor\frac{xk^l}{y}\rfloor=x-y\lfloor\frac{x}{y}\rfloor \]
考慮取整那一項不好處理,可以玩一波騷操作,兩邊對\(y\)取模:
\[ xk^l\equiv x\pmod{y} \]
因為\((x,y)=1\),所以兩邊除掉:
\[ k^l\equiv1\pmod{y} \]


可得:
\[ (k,y)=1 \]
所以答案可以形式化的寫成:
\[ \sum_{i=1}^n\sum_{j=1}^m[(i,j)=1][(j,k)=1] \]
然後嘗試著對\([(i,j)=1]\)進行莫比烏斯反演:
\[ \begin{align} ans=&\sum_{i=1}^n\sum_{j=1}^m[(j,k)=1]\sum_{d|i\&d|j}\mu(d)\\ =&\sum_{d=1}^{\min(n,m)}\mu(d)[(d,k)=1]\lfloor\frac{n}{d}\rfloor\sum_{j=1}^{\lfloor\frac{m}{d}\rfloor}[(j,k)=1] \end{align} \]
此處省略了中間交換求和符號之類的過程。

然後考慮下這個式子後面一塊,設:
\[ f(n)=\sum_{i=1}^n[(i,k)=1] \]
考慮分成幾塊,每塊長度都是\(k\),顯然:
\[ f(n)=\lfloor\frac{n}{k}\rfloor f(k)+f(n\bmod k) \]
再考慮下答案的前半段,由於後兩項要數論分塊,所以要求前兩項的字首和,設:
\[ g(n,k)=\sum_{d=1}^n\mu(d)[(d,k)=1] \]
對後面莫比烏斯反演下:
\[ \begin{align} g(n,k)=&\sum_{d=1}^n\mu(d)\sum_{t|d\&t|k}\mu(t)\\ =&\sum_{t=1}^n\mu(t)[t|k]\sum_{d=1}^{\lfloor\frac{n}{t}\rfloor}\mu(dt) \end{align} \]
然後最玄學的一步來了,,若想要使\(\mu(dt)\ne 0\),顯然\((d,t)=1\),此時\(\mu(dt)=\mu(d)\mu(t)\),所以後面一項可以寫成:
\[ \begin{align} g(n,k)=&\sum_{t=1}^n\mu(t)[t|k]\sum_{d=1}^{\lfloor\frac{n}{t}\rfloor}\mu(d)\mu(t)[(d,t)=1]\\ =&\sum_{t=1}^n\mu^2(t)[t|k]\sum_{d=1}^{\lfloor\frac{n}{t}\rfloor}\mu(d)[(d,t)=1]\\ =&\sum_{t=1}^n\mu^2(t)[t|k]g(\lfloor\frac{n}{d}\rfloor,t)\\ =&\sum_{t|k}\mu^2(t)g(\lfloor\frac{n}{d}\rfloor,t)\\ \end{align} \]
這是個遞迴的形式,預處理下\(k\)以內所有數的約數,直接記憶化暴力算就好了。

對於\(k=1\)的情況,\(g\)就變成了\(\sum_{i=1}^n \mu(i)\),這個可以杜教篩出來。

答案可以寫成:
\[ ans=\sum_{d=1}^{\min(n,m)}\mu(d)[(d,k)=1]\lfloor\frac{n}{d}\rfloor f(\lfloor\frac{m}{d}\rfloor) \]
後面數論分塊就行了。

#pragma GCC optimize(3)
#include<bits/stdc++.h>
using namespace std;

#ifdef ONLINE_JUDGE
#define getchar() ((p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin)),p1==p2)?EOF:*p1++)
#endif

namespace fast_IO {
    char buf[1<<21],*p1=buf,*p2=buf;

    template <typename T> inline void read(T &x) {
        x=0;T f=1;char ch=getchar();
        for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-f;
        for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0';x*=f;
    }
    template <typename T,typename... Args> inline void read(T& x,Args& ...args) {
        read(x),read(args...);
    }

    char buf2[1<<21],a[80];int p,p3=-1;

    inline void flush() {fwrite(buf2,1,p3+1,stdout),p3=-1;}
    template <typename T> inline void write(T x) {
        if(p3>(1<<20)) flush();
        if(x<0) buf2[++p3]='-',x=-x;
        do {a[++p]=x%10+48;} while(x/=10);
        do {buf2[++p3]=a[p];} while(--p);
        buf2[++p3]='\n';
    }
    template <typename T,typename... Args> inline void write(T x,Args ...args) {
        write(x),write(args...);
    }
}

using fast_IO :: read;
using fast_IO :: write;
using fast_IO :: flush;

#define ll long long 

const int maxn = 5e3+10;
const int N = 5e6+10;

int pri[N],tot,mu[N],vis[N],F[maxn],Mu[N];

void sieve() {
    mu[1]=1;
    for(int i=2;i<N;i++) {
        if(!vis[i]) pri[++tot]=i,mu[i]=-1;
        for(int j=1;j<=tot&&i*pri[j]<N;j++) {
            vis[i*pri[j]]=1;
            if(i%pri[j]==0) break;
            mu[i*pri[j]]=-mu[i];
        }
    }
    for(int i=1;i<N;i++) Mu[i]=Mu[i-1]+mu[i];
}

map<int ,int > mp;

int sum_mu(int n) {
    if(n<N) return Mu[n];
    if(mp[n]) return mp[n];
    int res=1,T=2;
    while(T<=n) {
        int pre=T;T=n/(n/T);
        res=res-(T-pre+1)*sum_mu(n/T);T++;
    }
    return mp[n]=res;
}

int dv[2001][201];
map<int ,int > G[2001];

int g(int n,int k) {
    if(k==1) return sum_mu(n);int ans=0;
    if(G[k][n]) return G[k][n];
    if(!n) return 0;
    for(int i=1;i<=dv[k][0];i++)
        ans+=abs(mu[dv[k][i]])*g(n/dv[k][i],dv[k][i]);
    return G[k][n]=ans;
}

int n,m,k;

int f(int x) {return F[k]*(x/k)+F[x%k];}

int main() {
    read(n,m,k);
    for(int i=1;i<=k;i++) F[i]=F[i-1]+(__gcd(i,k)==1);
    for(int i=1;i<=k;i++)
        for(int j=1;j<=i;j++)
            if(i%j==0) dv[i][++dv[i][0]]=j;
    sieve();int T=1;ll ans=0;
    while(T<=n&&T<=m) {
        int pre=T;T=min(n/(n/T),m/(m/T));
        ans=ans+1ll*(g(T,k)-g(pre-1,k))*(n/T)*f(m/T);T++;
    }write(ans);
    flush();
    return 0;
}