1. 程式人生 > >*LOJ#2085. 「NOI2016」循環之美

*LOJ#2085. 「NOI2016」循環之美

algorithm bre long 數量 ide PE 處理 GC 時有

$n \leq 1e9,m \leq 1e9,k \leq 2000$,求$k$進制下$\frac{x}{y}$有多少種不同的純循環數取值,$1 \leq x \leq n,1 \leq y \leq m$。純循環數是指小數點後直接就開始循環,整數也算。

與上個題的醜陋相比這個題不知道美到哪裏去。。雖然自己沒想出來。

提示說了,出現相同余數時有純循環。假設循環節是$l$,那麽$xk^l$和$x$除$y$會得到相同余數--同余!$xk^l \equiv x (\mod y)$。由於題目要互不相同的,所以$x$和$y$互質,有逆元。兩邊同乘$x$的逆元,得$k^l \equiv 1 (\mod y)$,存在這樣的$l$,由歐拉定理得只需$k$與$y$互質即可。可以開始推式子。

$\\ \sum_{x=1}^{n}\sum_{y=1}^m[(x,y)=1][(k,y)=1]$
$\\ =\sum_{y=1}^{m}[(k,y)=1]\sum_{x=1}^{n}[(x,y)=1]$
$\\ =\sum_{y=1}^{m}[(k,y)=1]\sum_{x=1}^{n}\sum_{d|x,d|y}\mu(d)$
$\\ =\sum_{y=1}^{m}[(k,y)=1]\sum_{d|y}\mu(d)\left \lfloor \frac{n}{d} \right \rfloor$
$\\ =\sum_{d=1}^{m}\mu(d)\left \lfloor \frac{n}{d} \right \rfloor\sum_{d|y,y \leq m}[(k,y)=1]$
$\\ =\sum_{d=1}^{m}\mu(d)\left \lfloor \frac{n}{d} \right \rfloor\sum_{i=1}^{\left \lfloor \frac{m}{d} \right \rfloor}[(k,i)=1][(k,d)=1]$
$\\ =\sum_{d=1}^{min(n,m)}\mu(d)\left \lfloor \frac{n}{d} \right \rfloor[(k,d)=1]\sum_{i=1}^{\left \lfloor \frac{m}{d} \right \rfloor}[(k,i)=1]$

可以發現$f(t)=\sum_{i=1}^t[(k,i)=1]=\left \lfloor \frac{t}{k} \right \rfloor f(k)+f(t \mod k)$。這樣就可以O(1)算後面那坨,在min(n,m)的時間內得到84分。

技術分享圖片
 1 //#include<iostream>
 2 #include<cstring>
 3 #include<cstdio>
 4 //#include<math.h>
 5 //#include<set>
 6 //#include<queue>
 7 //#include<bitset>
8 //#include<vector> 9 #include<algorithm> 10 #include<stdlib.h> 11 using namespace std; 12 13 #define LL long long 14 int qread() 15 { 16 char c; int s=0,f=1; while ((c=getchar())<0 || c>9) (c==-) && (f=-1); 17 do s=s*10+c-0; while ((c=getchar())>=0 && c<=9); return s*f; 18 } 19 20 //Pay attention to ‘-‘ , LL and double of qread!!!! 21 22 int n,m,K; 23 int f[2011]; 24 25 #define maxm 20000011 26 int miu[maxm],prime[maxm],lp=0; bool notprime[maxm]; 27 void pre(int n) 28 { 29 miu[1]=1; 30 for (int i=2;i<=n;i++) 31 { 32 if (!notprime[i]) {prime[++lp]=i; miu[i]=-1;} 33 for (int tmp,j=1;j<=lp && 1ll*i*prime[j]<=n;j++) 34 { 35 notprime[tmp=i*prime[j]]=1; 36 if (i%prime[j]) miu[tmp]=-miu[i]; 37 else {miu[tmp]=0; break;} 38 } 39 } 40 } 41 42 int gcd(int a,int b) {while (b^=a^=b^=a%=b); return a;} 43 int getf(int x) {return x/K*f[K]+f[x%K];} 44 45 int main() 46 { 47 n=qread(); m=qread(); K=qread(); 48 for (int i=1;i<=K;i++) f[i]=f[i-1]+(gcd(i,K)==1); 49 pre(min(n,m)); 50 LL ans=0; 51 for (int i=1,to=min(n,m);i<=to;i++) if ((i%K>0) && (f[i%K]-f[(i-1)%K])>0) 52 ans+=miu[i]*(n/i)*1ll*getf(m/i); 53 printf("%lld\n",ans); 54 return 0; 55 }
View Code

前面有個$\left \lfloor \frac{n}{d} \right \rfloor$是可以分塊枚舉的,如果能求快一點對所有$t=\left \lfloor \frac{n}{d} \right \rfloor$求出$g(t,k)=\sum_{i=1}^t[(i,k)=1]\mu(i)$就好了。

!$k=p^cq$,設$p$為$k$的一個質因子,則$k$可以這麽表示,其中$(q,p)=1$。要求與$k$互質的,那就求與$q$互質的,挑掉與$p$不互質和與$q$互質的。與$p$不互質的有一定是$p$的倍數,因為$p$是質數嘛。所以

$\\ g(t,k)=\sum_{i=1}^{t}[(k,i)=1]\mu(i)$
$\\ =\sum_{i=1}^{t}[(i,q)=1]\mu(i)-\sum_{j=1}^{\left \lfloor \frac{t}{p} \right \rfloor}[(jp,q)=1]\mu(jp)$
$\\ =g(t,q)-\mu(p)g(\left \lfloor \frac{t}{p} \right \rfloor,q)$
$\\ =g(t,q)+g(\left \lfloor \frac{t}{p} \right \rfloor,q)$

建立一個根號復雜度的遞推關系,$k$那一維的數量是常數級別的,畢竟2000以內質因子最多的數也沒幾個質因子。遞歸邊界的話,如果t=0直接返回0,如果k=1那就是$\mu$的前綴和,需要再寫一個杜教篩對所有n除以d的下取整數值的$\mu$前綴和處理出來。

*LOJ#2085. 「NOI2016」循環之美