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

$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$前缀和处理出来。

posted @ 2018-07-02 12:40  Blue233333  阅读(235)  评论(0编辑  收藏  举报