【NOI2016】循环之美【莫比乌斯反演】【整除分块】【杜教筛】【类杜教筛】

传送门

题意:给定 n , m , k n,m,k n,m,k,求 1 ≤ x ≤ n , 1 ≤ y ≤ m 1\leq x\leq n,1\leq y\leq m 1xn,1ym x y x \over y yx中数值不同的纯循环小数或整数的个数。

n ≤ 1 0 9 , m ≤ 1 0 9 , k ≤ 2 × 1 0 3 n \leq 10^9,m\leq10^9,k\leq2\times10^3 n109,m109,k2×103

显然只需要考虑最简分数即 gcd ⁡ ( x , y ) = 1 \gcd(x,y)=1 gcd(x,y)=1的情况

容(bu)易(yong)证明, x y x\over y yx满足题意当且仅当 gcd ⁡ ( y , k ) = 1 \gcd(y,k)=1 gcd(y,k)=1

所以

A n s = ∑ i = 1 N ∑ j = 1 M [ gcd ⁡ ( i , j ) = 1 ] [ gcd ⁡ ( j , k ) = 1 ] Ans=\sum_{i=1}^N\sum_{j=1}^M[\gcd(i,j)=1][\gcd(j,k)=1] Ans=i=1Nj=1M[gcd(i,j)=1][gcd(j,k)=1]

换下顺序

= ∑ j = 1 M [ gcd ⁡ ( j , k ) = 1 ] ∑ i = 1 N [ gcd ⁡ ( i , j ) = 1 ] =\sum_{j=1}^M[\gcd(j,k)=1]\sum_{i=1}^N[\gcd(i,j)=1] =j=1M[gcd(j,k)=1]i=1N[gcd(i,j)=1]

把后面反演掉,前面不管

= ∑ j = 1 M [ gcd ⁡ ( j , k ) = 1 ] ∑ i = 1 N ∑ d ∣ i , d ∣ j μ ( d ) =\sum_{j=1}^M[\gcd(j,k)=1]\sum_{i=1}^N\sum_{d\mid i,d\mid j}\mu(d) =j=1M[gcd(j,k)=1]i=1Ndi,djμ(d)

枚举 d d d

= ∑ d = 1 N ⌊ N d ⌋ μ ( d ) ∑ d ∣ j j ≤ M [ gcd ⁡ ( j , k ) = 1 ] =\sum_{d=1}^N\lfloor \frac{N}{d}\rfloor\mu(d)\sum_{d\mid j}^{j\leq M}[\gcd(j,k)=1] =d=1NdNμ(d)djjM[gcd(j,k)=1]

换成枚举 j j j d d d的多少倍

= ∑ d = 1 N ⌊ N d ⌋ μ ( d ) ∑ j = 1 ⌊ M d ⌋ [ gcd ⁡ ( j d , k ) = 1 ] =\sum_{d=1}^N\lfloor \frac{N}{d}\rfloor\mu(d)\sum_{j=1}^{\lfloor\frac{M}d{}\rfloor}[\gcd(jd,k)=1] =d=1NdNμ(d)j=1dM[gcd(jd,k)=1]

gcd ⁡ \gcd gcd拆开

= ∑ d = 1 N ⌊ N d ⌋ μ ( d ) ∑ j = 1 ⌊ M d ⌋ [ gcd ⁡ ( j , k ) = 1 ] [ gcd ⁡ ( d , k ) = 1 ] =\sum_{d=1}^N\lfloor \frac{N}{d}\rfloor\mu(d)\sum_{j=1}^{\lfloor\frac{M}d{}\rfloor}[\gcd(j,k)=1][\gcd(d,k)=1] =d=1NdNμ(d)j=1dM[gcd(j,k)=1][gcd(d,k)=1]

= ∑ d = 1 N ⌊ N d ⌋ μ ( d ) [ gcd ⁡ ( d , k ) = 1 ] ∑ j = 1 ⌊ M d ⌋ [ gcd ⁡ ( j , k ) = 1 ] =\sum_{d=1}^N\lfloor \frac{N}{d}\rfloor\mu(d)[\gcd(d,k)=1]\sum_{j=1}^{\lfloor\frac{M}d{}\rfloor}[\gcd(j,k)=1] =d=1NdNμ(d)[gcd(d,k)=1]j=1dM[gcd(j,k)=1]

f ( n , k ) = ∑ i = 1 n [ gcd ⁡ ( i , k ) = 1 ] f(n,k)=\sum_{i=1}^n[\gcd(i,k)=1] f(n,k)=i=1n[gcd(i,k)=1]

由于 k k k只有 2000 2000 2000,并且 gcd ⁡ ( i , k ) = gcd ⁡ ( i % k , k ) \gcd(i,k)=\gcd(i\%k,k) gcd(i,k)=gcd(i%k,k),所以瞎预处理一下就可以算出来

这样

A n s = ∑ d = 1 N ⌊ N d ⌋ μ ( d ) [ gcd ⁡ ( d , k ) = 1 ] f ( ⌊ M d ⌋ , k ) Ans=\sum_{d=1}^N\lfloor \frac{N}{d}\rfloor\mu(d)[\gcd(d,k)=1]f(\lfloor\frac{M}{d}\rfloor,k) Ans=d=1NdNμ(d)[gcd(d,k)=1]f(dM,k)

这是个整除分块的形式,我们只需要想办法算出

g ( n , k ) = ∑ i = 1 n μ ( i ) [ gcd ⁡ ( i , k ) = 1 ] g(n,k)=\sum_{i=1}^n\mu(i)[\gcd(i,k)=1] g(n,k)=i=1nμ(i)[gcd(i,k)=1]

这个可以反演

g ( n , k ) = ∑ i = 1 n μ ( i ) ∑ d ∣ i , d ∣ k μ ( d ) g(n,k)=\sum_{i=1}^n\mu(i)\sum_{d\mid i,d\mid k}\mu(d) g(n,k)=i=1nμ(i)di,dkμ(d)

枚举 d d d

g ( n , k ) = ∑ d ∣ k μ ( d ) ∑ d ∣ i i ≤ n μ ( i ) g(n,k)=\sum_{d\mid k}\mu(d)\sum_{d\mid i}^{i\leq n}\mu(i) g(n,k)=dkμ(d)diinμ(i)

枚举倍数

g ( n , k ) = ∑ d ∣ k μ ( d ) ∑ i = 1 ⌊ n d ⌋ μ ( i d ) g(n,k)=\sum_{d\mid k}\mu(d)\sum_{i=1}^{\lfloor\frac{n}{d}\rfloor}\mu(id) g(n,k)=dkμ(d)i=1dnμ(id)

前方高能

观察 μ ( i d ) \mu(id) μ(id),发现如果 gcd ⁡ ( i , d ) ≠ 1 \gcd(i,d)\neq1 gcd(i,d)=1, μ ( i d ) \mu(id) μ(id)一定等于 0 0 0

所以……我们可以丢一个 [ gcd ⁡ ( i , d ) = 1 ] [\gcd(i,d)=1] [gcd(i,d)=1]进去

g ( n , k ) = ∑ d ∣ k μ ( d ) ∑ i = 1 ⌊ n d ⌋ μ ( i d ) [ gcd ⁡ ( i , d ) = 1 ] g(n,k)=\sum_{d\mid k}\mu(d)\sum_{i=1}^{\lfloor\frac{n}{d}\rfloor}\mu(id)[\gcd(i,d)=1] g(n,k)=dkμ(d)i=1dnμ(id)[gcd(i,d)=1]

由于 μ \mu μ是积性函数,有这个限制就可以拆开了,然后把 μ ( d ) \mu(d) μ(d)丢前面去

g ( n , k ) = ∑ d ∣ k μ 2 ( d ) ∑ i = 1 ⌊ n d ⌋ μ ( i ) [ gcd ⁡ ( i , d ) = 1 ] g(n,k)=\sum_{d\mid k}\mu^2(d)\sum_{i=1}^{\lfloor\frac{n}{d}\rfloor}\mu(i)[\gcd(i,d)=1] g(n,k)=dkμ2(d)i=1dnμ(i)[gcd(i,d)=1]

后面那个有没有很眼熟?

g ( n , k ) = ∑ d ∣ k μ 2 ( d ) g ( ⌊ n d ⌋ , d ) g(n,k)=\sum_{d\mid k}\mu^2(d)g(\lfloor\frac{n}{d}\rfloor,d) g(n,k)=dkμ2(d)g(dn,d)

然后就可以递归了

边界:

n = 0 n=0 n=0时, g ( n , k ) = 0 g(n,k)=0 g(n,k)=0

k = 1 k=1 k=1时,我们发现无法递归

所以写个杜教筛算一下就可以了

复杂度 O ( 能 过 ) O(能过) O()

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cctype>
#include <utility>
#include <map>
using namespace std;
typedef long long ll;
const int N=1e6;
int n,m,k;
int np[N+5],pl[N],cnt;
int mu[N+5],sum[N+5];
void init()
{
     np[1]=mu[1]=1;
     for (int i=2;i<=N;i++)
     {
	  if (!np[i]) pl[++cnt]=i,mu[i]=-1;
	  for (int j=1,x;(x=i*pl[j])<=N;j++)
	  {
	       np[x]=1;
	       if (i%pl[j]==0)
	       {
		    mu[x]=0;
		    break;
	       }
	       else mu[x]=-mu[i];
	  }
     }
     for (int i=1;i<=N;i++) sum[i]=sum[i-1]+mu[i];
}
map<int,int> ms;
int getms(int n)
{
     if (n<=N) return sum[n];
     map<int,int>::iterator p;
     if ((p=ms.find(n))!=ms.end()) return p->second;
     int ans=1;
     for (int l=2,r;l<=n;l=r+1)
     {
	  r=n/(n/l);
	  ans-=(r-l+1)*getms(n/l);
     }
     ms.insert(make_pair(n,ans));
     return ans;
}
int x[2005];
int gcd(int a,int b){return b? gcd(b,a%b):a;}
inline int f(const int& n){return n/k*x[k]+x[n%k];}
map<pair<int,int>,int> sg;
int g(int n,int k)
{
     if (n==0) return 0;
     if (k==1) return getms(n);
     map<pair<int,int>,int>::iterator p;
     pair<int,int> pi=make_pair(n,k);
     if ((p=sg.find(pi))!=sg.end()) return p->second;
     int ans=0;
     for (int i=1;i*i<=k;i++)
	  if (k%i==0)
	  {
	       if (mu[i]) ans+=g(n/i,i);
	       if (i*i<k&&mu[k/i]) ans+=g(n/(k/i),k/i);
	  }
     sg.insert(make_pair(pi,ans));
     return ans;
}
int main()
{
     init();
     scanf("%d%d%d",&n,&m,&k);
     for (int i=1;i<=k;i++) x[i]=x[i-1]+(gcd(i,k)==1);
     ll ans=0;
     for (int l=1,r;l<=n&&l<=m;l=r+1)
     {
	  r=min(n/(n/l),m/(m/l));
	  ans+=(ll)(n/l)*f(m/l)*(g(r,k)-g(l-1,k));
     }
     printf("%lld\n",ans);
     return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值