[NOI2016]循环之美(杜教筛)

首先要求每个数互不相等,故有$xperp y$。

可以发现$frac{x}{y}$在$k$进制下为纯循环小数的充要条件为$xcdot k^{len}equiv x(mod y)$,即$yperp k$。

接下来进行经典的推导:
$$egin{aligned}&sumlimits_{i=1}^{n}sumlimits_{j=1}^{m}[iperp j][jperp k]\=&sumlimits_{i=1}^{n}sumlimits_{j=1}^{m}sum_{d|i,d|j}mu(d)[jperp k]\=&sumlimits_{d=1}^{min(n,m)}mu(d)sumlimits_{i=1}^{lfloorfrac{n}{d} floor}sumlimits_{j=1}^{lfloorfrac{m}{d} floor}[jdperp k]\=&sumlimits_{d=1}^{min(n,m)}mu(d)lfloorfrac{n}{d} floorsumlimits_{j=1}^{lfloorfrac{m}{d} floor}[jperp k][dperp k]end{aligned}$$

令$f(n)=sumlimits_{i=1}^{n}[iperp k]$,由于$gcd(i,k)=gcd(i-k,k)$,故$f(n)=lfloorfrac{n}{k} floorcdot f(k)+f(n\%k)$

再令$g(i)=mu(i)[iperp k]$,则答案为$sumlimits_{d=1}^{min(n,m)}g(d)f(lfloorfrac{m}{d} floor)lfloorfrac{n}{d} floor$

令$G(n,k)$为$g()$的前缀和,同样进行根号优化:
$$G(n,k)=sumlimits_{i=1}^{n}mu(i)[iperp k]=sumlimits_{i=1}^{n}mu(i)sumlimits_{d|i,d|k}mu(d)=sumlimits_{d|k}mu(d)sumlimits_{i=1}^{lfloorfrac{n}{d} floor}mu(id)$$
注意到$mu(id)=[iperp d] ? 0:mu(i)cdot mu(d)$,故
$$G(n,k)=sumlimits_{d|k}mu^2(d)sumlimits_{i=1}^{lfloorfrac{n}{d} floor}mu(i)[iperp d]=sumlimits_{d|k}mu^2(d)G(lfloorfrac{n}{d} floor,d)$$

$G$函数可以通过递归记忆化求出。由于$G(a,b)$中$a$最多有$sqrt{n}$种取值,$b$最多有$sigma_0(k)$种取值,每次转移的复杂度也是$sigma_0(k)$的,故总复杂度为$O(n^{frac{2}{3}}+sigma_0^2(k))$,事实上远远达不到这个上界。

在做除$mu$和$varphi$之外的杜教筛时,用map会简洁得多,有时候(可能询问到的n不能确定时)必须用map。

此题还有一种更优类似洲阁筛的做法,能处理$kleqslant 10^{18}$的问题,复杂度为$O(n^{frac{2}{3}}+omega(k)sqrt{n})$。

https://blog.sengxian.com/solutions/bzoj-4652

 1 #include<map>
 2 #include<cstdio>
 3 #include<algorithm>
 4 #define rep(i,l,r) for (int i=(l); i<=(r); i++)
 5 typedef long long ll;
 6 using namespace std;
 7 
 8 const int N=5000010,K=2010;
 9 ll ans;
10 bool b[N];
11 int tot,w,n,m,k,F[K],p[N],miu[N];
12 map<int,int>G[K],Miu;
13 
14 int gcd(int a,int b){ return b ? gcd(b,a%b) : a; }
15 int f(int n){ return (n/k)*F[k]+F[n%k]; }
16 
17 void pre(int n){
18     miu[1]=1;
19     rep(i,2,n){
20         if (!b[i]) p[++tot]=i,miu[i]=-1;
21         for (int j=1; j<=tot && i*p[j]<=n; j++){
22             b[i*p[j]]=1;
23             if (i%p[j]==0) { miu[i*p[j]]=0; break; }
24                 else miu[i*p[j]]=-miu[i];
25         }
26     }
27     rep(i,2,n) miu[i]+=miu[i-1];
28 }
29 
30 int Mu(int n){
31     int res=1;
32     if (n<=w) return miu[n];
33     if (Miu.count(n)) return Miu[n];
34     for (int i=2,lst; i<=n; i=lst+1)
35         lst=n/(n/i),res-=Mu(n/i)*(lst-i+1);
36     return Miu[n]=res;
37 }
38 
39 int g(int n,int k){
40     int res=0;
41     if (!n) return 0;
42     if (G[k].count(n)) return G[k][n];
43     if (k==1) return Mu(n);
44     for (int d=1; d*d<=k; d++)
45         if (k%d==0){
46             if (miu[d]-miu[d-1]) res+=g(n/d,d);
47             if (d*d==k) continue;
48             int t=k/d;
49             if (miu[t]-miu[t-1]) res+=g(n/t,t);
50         }
51     return G[k][n]=res;
52 }
53 
54 int main(){
55     freopen("cycle.in","r",stdin);
56     freopen("cycle.out","w",stdout);
57     scanf("%d%d%d",&n,&m,&k); w=min(max(n,m),5000000); pre(w);
58     rep(i,1,k) F[i]=F[i-1]+(gcd(i,k)==1);
59     for (int i=1,lst; i<=m && i<=n; i=lst+1)
60         lst=min(n/(n/i),m/(m/i)),ans+=1ll*(g(lst,k)-g(i-1,k))*f(m/i)*(n/i);
61     printf("%lld
",ans);
62     return 0;
63 }
原文地址:https://www.cnblogs.com/HocRiser/p/9494834.html