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

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

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

接下来进行经典的推导:
$$\begin{aligned}&\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{m}[i\perp j][j\perp k]\\=&\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{m}\sum_{d|i,d|j}\mu(d)[j\perp k]\\=&\sum\limits_{d=1}^{min(n,m)}\mu(d)\sum\limits_{i=1}^{\lfloor\frac{n}{d}\rfloor}\sum\limits_{j=1}^{\lfloor\frac{m}{d}\rfloor}[jd\perp k]\\=&\sum\limits_{d=1}^{min(n,m)}\mu(d)\lfloor\frac{n}{d}\rfloor\sum\limits_{j=1}^{\lfloor\frac{m}{d}\rfloor}[j\perp k][d\perp k]\end{aligned}$$

令$f(n)=\sum\limits_{i=1}^{n}[i\perp k]$,由于$gcd(i,k)=gcd(i-k,k)$,故$f(n)=\lfloor\frac{n}{k}\rfloor\cdot f(k)+f(n\%k)$

再令$g(i)=\mu(i)[i\perp k]$,则答案为$\sum\limits_{d=1}^{min(n,m)}g(d)f(\lfloor\frac{m}{d}\rfloor)\lfloor\frac{n}{d}\rfloor$

令$G(n,k)$为$g()$的前缀和,同样进行根号优化:
$$G(n,k)=\sum\limits_{i=1}^{n}\mu(i)[i\perp k]=\sum\limits_{i=1}^{n}\mu(i)\sum\limits_{d|i,d|k}\mu(d)=\sum\limits_{d|k}\mu(d)\sum\limits_{i=1}^{\lfloor\frac{n}{d}\rfloor}\mu(id)$$
注意到$\mu(id)=[i\perp d]\ ?\ 0:\mu(i)\cdot \mu(d)$,故
$$G(n,k)=\sum\limits_{d|k}\mu^2(d)\sum\limits_{i=1}^{\lfloor\frac{n}{d}\rfloor}\mu(i)[i\perp d]=\sum\limits_{d|k}\mu^2(d)G(\lfloor\frac{n}{d}\rfloor,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。

此题还有一种更优类似洲阁筛的做法,能处理$k\leqslant 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\n",ans);
62     return 0;
63 }

 

posted @ 2018-08-17 18:32  HocRiser  阅读(294)  评论(0编辑  收藏  举报