UOJ #221 【NOI2016】 循環之美

題目連接:循環之美php

  這道題感受很是優美……能有一個這麼優美的題面和較高的思惟難度真的不容易……ios

  爲了表示方便,讓我先講一下兩個符號。\([a]\)表示若是\(a\)爲真,那麼返回\(1\),不然返回\(0\); \(a \perp b\)表示\(a\)與\(b\)互質。函數

  首先,咱們須要考慮一個分數要成爲純循環小數須要知足什麼條件。優化

  咱們先來回想一下,咱們是怎樣使用除法來判斷一個分數$\frac{x}{y}$是不是純循環小數的。顯然咱們是一路除下去,何時出現了相同的餘數,那麼這個數就是一個循環小數。若是第一個重複的餘數是$x$,那麼這個數就是純循環小數。這種方法實際上就是存在一個數$l\neq 0$,使得:$$xk^l \equiv x (\bmod y)$$ui

  又因爲題目要求值不重複,那麼咱們能夠獲得$x \perp y$。因此,咱們能夠推出$k^l \equiv 1(\bmod y)$。因此咱們只需$k \perp y$便可。spa

  因而,咱們其實是要求這個式子:blog

\begin{aligned}&\sum_{x=1}^{n} \sum_{y=1}^{m}[x\perp y][y \perp k]\\=&\sum_{y=1}^{m}[y\perp k]\sum_{x=1}^{n}[x\perp y]\end{aligned}遞歸

  用一下莫比烏斯反演,能夠獲得:get

\begin{aligned}&\sum_{y=1}^{m}[y\perp k] \sum_{x=1}^{n}\sum_{d|x,d|y}\mu(d)\\=&\sum_{y=1}^{m}[y\perp k]\sum_{d|y}^{n}\mu(d)\lfloor \frac{n}{d}\rfloor \end{aligned}string

  再經過換一下枚舉順序,可得:

\begin{aligned}&\sum_{d=1}^{n}\mu(d)\lfloor \frac{n}{d} \rfloor \sum_{y=1}^{m}[d\ |\ y][y\perp k]\\=&\sum_{d=1}^{n}\mu(d)\lfloor \frac{n}{d}\rfloor \sum_{i=1}^{\lfloor \frac{m}{d} \rfloor}[id\perp k]\\=& \sum_{d=1}^{n}[d\perp k]\mu(d)\lfloor \frac{n}{d}\rfloor \sum_{i=1}^{\lfloor \frac{m}{d} \rfloor}[i\perp k]\end{aligned}

  最後一步用了互質的一個性質,那就是$[ab\perp c]=[a\perp c][b\perp c]$。這點應該很好理解吧。

  咱們不妨設$f(n,k)=\sum_{i=1}^n[i\perp k]$,那麼咱們只要在$O(1)$的時間內求出$f$,複雜度就是$O(n)$,就能夠獲得$84$分。實際上,咱們能夠獲得一個式子:

$$f(n,k)=\lfloor \frac{n}{k} \rfloor f(k,k)+f(n\bmod k,k)$$

  這個應該也不難理解,由於$[a\perp b]=[a\bmod b \perp b]$

  因而咱們就能夠對$f(n,k)$預處理出$n\le k$的值,每次求值就變成$O(1)$了。因而$84$分到手了。

  咱們考慮接下來該如何優化。因爲$\lfloor \frac{m}{x} \rfloor$只有$\sqrt{m}$種取值,$\lfloor \frac{n}{x} \rfloor$只有$\sqrt{n}$種取值,因而咱們顯然能夠分段求和。而後,咱們就須要快速求出$\sum_{i=1}^n[i\perp k]\mu(i)$的值。

  不妨設$g(n,k)=\sum_{i=1}^n[i\perp k]\mu(i)$,咱們來考慮一下這個函數如何快速求。咱們先考慮$k$的一個質因數$p$,那麼$k$顯然能夠寫成$p^cq$的形式。因爲在$[1,n]$的範圍中只有與$k$互質的纔是有效值,那麼若$x\perp k$,咱們能夠獲得$x\perp p$而且$x\perp q$。因而,咱們能夠考慮從$x\perp q$的取值中減去$x$不與$p$互質的部分,就能夠獲得$x\perp k$的部分。這裏若是不懂的話能夠本身畫一個$x\perp q$,$x\perp p$,$x\perp k$的關係圖理解一下。

  因爲全部與$q$互質的數必定能夠寫成$p^xy(y\perp q)$的形式。那麼咱們須要減去的數必定知足$x>0$。又因爲當$x>1$時$\mu(p^xy)=0$,因此咱們只須要考慮$x=1$的狀況便可。在這種狀況下,咱們須要考慮的數就是$py(y\perp q)$的形式,因此咱們能夠獲得以下式子:\begin{aligned}  g(n,k)&=\sum_{i=1}^n[i\perp q]\mu(i)-\sum_{y=1}^{\lfloor\frac{n}{p}\rfloor}[py\perp q]\mu(py) \\&=g(n,q)- \sum_{y=1}^{\lfloor\frac{n}{p}\rfloor}[y\perp q]\mu(py)\end{aligned}

  上面的最後一步是因爲$p\perp q$,因此$py\perp q$只需在保證$y\perp q$便可。

  咱們接着來考慮後一個式子。顯然當$p\perp y$的時候$\mu(py)=\mu(p)\mu(y)$,不然$\mu(py)=0$。因而,咱們又獲得了:

\begin{aligned} g(n,k)&=g(n,q)- \sum_{y=1}^{\lfloor\frac{n}{p}\rfloor}[y\perp p][y\perp q]\mu(p)\mu(y)\\&=g(n,q)-\mu(p)\sum_{y=1}^{\lfloor\frac{n}{p}\rfloor}[y\perp k]\mu(y)\\&=g(n,q)+g(\lfloor\frac{n}{p}\rfloor,k) \end{aligned}

  因而咱們就能夠遞歸求解了。容易發現邊界狀況就是$n=0$或者$k=1$。$n=0$的時候直接返回$0$就能夠了,$k=1$的時候就是莫比烏斯函數的前綴和,用杜教篩求出來就能夠了。因爲每次遞歸要麼$n$會變成$\lfloor \frac{n}{p} \rfloor$,有$O(\sqrt{n})$種取值;要麼$p$少了一個質因數,有$\omega(k)$種取值,因此總共有$O(\omega(k)\sqrt{n})$種取值,記憶化搜索便可。其中$\omega(k)$表示$k$的不一樣的質因子數目。因而最後的總複雜度爲$O(\omega(k)\sqrt{n}+n^{\frac{2}{3}})$,能夠經過此題。

  兩個細節:

  能夠在遞歸求$g$的時候不直接記$k$,而是記錄$k$還剩下多少個質因數,方便存儲;

  記憶化的時候能夠開哈希鏈表存儲,用$map$也能夠,或者乾脆分$n,m$以及小於、大於根號的狀況存儲也能夠。

  第一次寫這種題的題解,好像寫得過於詳細了(實際上是由於我什麼都不會),請大神們不要噴……

  下面貼代碼:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define File(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout)
#define maxn 5000010
#define mod 100007
#define maxk 2010
 
using namespace std;
typedef long long llg;
 
int n,m,k,pr_k[maxk],lp;
int s[maxn],ls,f[maxk];
int head[mod],next[maxn],to[maxn],tt;
int h1[11][mod],n1[maxn],t1[maxn],t2;
bool vis[maxn],huzhi[maxk];
llg ans,zto[maxn],zt1[maxn],mu[maxn];
 
int gcd(int a,int b){
    int r=a%b;
    while(r) a=b,b=r,r=a%b;
    return b;
}
 
void pre(){
    mu[1]=1;
    for(int i=2;i<maxn;i++){
        if(!vis[i]) s[++ls]=i,mu[i]=-1;
        for(int j=1;j<=ls && s[j]*i<maxn;j++){
            vis[s[j]*i]=1;
            if(i%s[j]) mu[i*s[j]]=-mu[i];
            else{mu[i*s[j]]=0;break;}
        }
    }
    for(int i=2;i<maxn;i++) mu[i]+=mu[i-1];
    for(int i=1;i<=k;i++){
        huzhi[i]=(gcd(i,k)==1);
        f[i]=f[i-1]+huzhi[i];
    }
}
 
llg suan(int x){return (x/k)*f[k]+f[x%k];}
llg solveu(int x){
    if(x<maxn) return mu[x];
    for(int i=head[x%mod];i;i=next[i]) if(to[i]==x) return zto[i];
    int now=++tt; to[tt]=x;next[tt]=head[x%mod];head[x%mod]=tt; zto[now]=1;
    for(int i=2,nt=0;nt<x;i=nt+1) nt=x/(x/i),zto[now]-=(nt-i+1)*solveu(x/i);
    return zto[now];
}
 
llg g(int x,int y){
    if(!x) return solveu(y);
    if(y<=1) return y;
    for(int i=h1[x][y%mod];i;i=n1[i]) if(t1[i]==y) return zt1[i];
    int now=++t2; t1[t2]=y;n1[t2]=h1[x][y%mod];h1[x][y%mod]=t2;
    return zt1[now]=g(x-1,y)+g(x,y/pr_k[x]);
}
 
int main(){
    File("a");
    scanf("%d %d %d",&n,&m,&k);
    pre();
    for(int i=1;s[i]<=k;i++) if(k%s[i]==0) pr_k[++lp]=s[i];
    for(int d=1,l=min(n,m),nt;d<=l;d=nt+1){
        nt=min(n/(n/d),m/(m/d));
        ans+=(g(lp,nt)-g(lp,d-1))*(llg)(n/d)*suan(m/d);
    }
    printf("%lld",ans);
    return 0;
}

   最後附一個BZOJ的提交網址:BZOJ 4652 循環之美

相關文章
相關標籤/搜索