NOI 2016 循环之美 题解

题目传送门

题目大意: 问有多少个值不相同的分数 x y ( x ∈ [ 1 , n ] , y ∈ [ 1 , m ] ) \dfrac x y(x\in[1,n],y\in[1,m]) yx(x[1,n],y[1,m]),满足在 k k k 进制下是个纯循环小数。纯循环:即小数存在循环节(可以是 0 0 0),且从小数点后第一位就开始循环。

题解

假设 k k k 10 10 10,循环节为 x 1 x 2 . . . x c x_1x_2...x_c x1x2...xc,那么小数点部分就可以表示为这样的分数: x 1 x 2 . . . x c 999...99 \dfrac {x_1x_2...x_c} {999...99} 999...99x1x2...xc,下面是 c c c 9 9 9

也就是说,对于分数 x y \dfrac x y yx,只有当 y y y 的某个倍数是若干个 9 9 9 时, x y \dfrac x y yx 才是纯循环小数。

c c c 9 9 9 组成的数可以表示成 1 0 c − 1 10^c-1 10c1,也就是要存在一个 c c c 满足 y ∣ ( 1 0 c − 1 ) y|(10^c-1) y(10c1),这个东西可以转化成 1 0 c ≡ 1 ( m o d y ) 10^c\equiv 1\pmod y 10c1(mody),根据扩欧,只有当 10 10 10 y y y 互质时, 10 10 10 才存在逆元,也就是存在满足条件的 c c c

类似的,我们可以得到,只有当 y ⊥ k y\perp k yk 时, x y \dfrac x y yx 才是纯循环小数。

那么此时已经可以写出答案式子了:
∑ i = 1 n ∑ j = 1 m [ i ⊥ j ] [ j ⊥ k ] \sum_{i=1}^n\sum_{j=1}^m[i\perp j][j\perp k] i=1nj=1m[ij][jk]

[ i ⊥ j ] [i\perp j] [ij] 使用莫反,得到:
= ∑ i = 1 n ∑ j = 1 m [ j ⊥ k ] ∑ d ∣ gcd ⁡ ( i , j ) μ ( d ) = ∑ d = 1 min ⁡ ( n , m ) μ ( d ) ∑ i = 1 n [ d ∣ i ] ∑ j = 1 m [ d ∣ j ] [ j ⊥ k ] = ∑ d = 1 min ⁡ ( n , m ) μ ( d ) [ d ⊥ k ] ⌊ n d ⌋ ∑ j = 1 ⌊ m d ⌋ [ j ⊥ k ] \begin{aligned} &=\sum_{i=1}^n\sum_{j=1}^m [j\perp k]\sum_{d|\gcd(i,j)}\mu(d)\\ &=\sum_{d=1}^{\min(n,m)}\mu(d)\sum_{i=1}^n[d|i]\sum_{j=1}^m[d|j][j\perp k]\\ &=\sum_{d=1}^{\min(n,m)}\mu(d) [d\perp k] \lfloor \frac n d \rfloor\sum_{j=1}^{\lfloor \frac m d \rfloor}[j\perp k] \end{aligned} =i=1nj=1m[jk]dgcd(i,j)μ(d)=d=1min(n,m)μ(d)i=1n[di]j=1m[dj][jk]=d=1min(n,m)μ(d)[dk]dnj=1dm[jk]

一开始尝试往另外两个互质上套莫反,然而发现会越推越丑……所以直接考虑从这个式子入手求解。

注意到里面有一个 ⌊ n d ⌋ \lfloor \dfrac n d\rfloor dn,这提示我们进行整除分块,假如能对另外两个部分进行快速的前缀求和,那么就可以套整除分块了。

g ( n , k ) = ∑ i = 1 n μ ( i ) [ i ⊥ k ] , f ( n , k ) = ∑ i = 1 n [ i ⊥ k ] g(n,k)=\sum_{i=1}^n \mu(i)[i\perp k],f(n,k)=\sum_{i=1}^n [i\perp k] g(n,k)=i=1nμ(i)[ik],f(n,k)=i=1n[ik],注意到如果 k k k > 1 >1 >1 个质因子 p i p_i pi,那么只保留一个其他全部除掉是不会影响这两个式子的,分析一下互质的性质就能发现,于是我们预处理一下这个 k k k,现在 k k k 满足不包含平方因子。

f , g f,g f,g 没有什么很好的直接求的方法,所以只能尝试递推。对于 k k k 的一个质因子 p p p,考虑除掉 p p p 之后答案的变化,先看 f f f
f ( n , k ) = ∑ i = 1 n [ i ⊥ k ] = ∑ i = 1 n [ i ⊥ k p ] − ∑ i = 1 n [ p ∣ i ] [ i ⊥ k p ] = f ( n , k p ) − ∑ i = 1 ⌊ n p ⌋ [ i p ⊥ k p ] = f ( n , k p ) − ∑ i = 1 ⌊ n p ⌋ [ i ⊥ k p ] = f ( n , k p ) − f ( ⌊ n p ⌋ , k p ) \begin{aligned} f(n,k)&=\sum_{i=1}^n [i\perp k]\\ &=\sum_{i=1}^n [i\perp \frac k p]-\sum_{i=1}^n [p|i][i\perp \frac k p]\\ &=f(n,\frac k p)-\sum_{i=1}^{\lfloor \frac n p \rfloor}[ip\perp \frac k p]\\ &=f(n,\frac k p)-\sum_{i=1}^{\lfloor \frac n p \rfloor}[i\perp \frac k p]\\ &=f(n,\frac k p)-f(\lfloor \frac n p\rfloor,\frac k p) \end{aligned} f(n,k)=i=1n[ik]=i=1n[ipk]i=1n[pi][ipk]=f(n,pk)i=1pn[ippk]=f(n,pk)i=1pn[ipk]=f(n,pk)f(pn,pk)

中间 [ i p ⊥ k p ] = [ i ⊥ k p ] [ip\perp \dfrac k p]=[i\perp \dfrac k p] [ippk]=[ipk] 这一步就用到了k没有平方因子这个性质。

g g g 的递推是类似的:
g ( n , k ) = ∑ i = 1 n μ ( i ) [ i ⊥ k ] = ∑ i = 1 n μ ( i ) [ i ⊥ k p ] − ∑ i = 1 n μ ( i ) [ p ∣ i ] [ i ⊥ k p ] = g ( n , k p ) − ∑ i = 1 ⌊ n p ⌋ μ ( i p ) [ i p ⊥ k p ] \begin{aligned} g(n,k)&=\sum_{i=1}^n \mu(i)[i\perp k]\\ &=\sum_{i=1}^n \mu(i)[i\perp \frac k p]-\sum_{i=1}^n\mu(i)[p|i][i\perp \frac k p]\\ &=g(n,\frac k p)-\sum_{i=1}^{\lfloor \frac n p \rfloor} \mu(ip)[ip\perp \frac k p] \end{aligned} g(n,k)=i=1nμ(i)[ik]=i=1nμ(i)[ipk]i=1nμ(i)[pi][ipk]=g(n,pk)i=1pnμ(ip)[ippk]

μ \mu μ 有一个性质, μ ( i j ) = μ ( i ) μ ( j ) [ i ⊥ j ] \mu(ij)=\mu(i)\mu(j)[i\perp j] μ(ij)=μ(i)μ(j)[ij],证明的话,若不满足 i ⊥ j i\perp j ij,那么 i j ij ij 就含有平方因子,则 μ ( i j ) = 0 \mu(ij)=0 μ(ij)=0;如果满足那么根据积性函数定义就显然可以拆开。

代入得到:
= g ( n , k p ) − ∑ i = 1 ⌊ n p ⌋ μ ( i ) μ ( p ) [ i ⊥ p ] [ i ⊥ k p ] = g ( n , k p ) + ∑ i = 1 ⌊ n p ⌋ μ ( i ) [ i ⊥ k ] = g ( n , k p ) + g ( ⌊ n p ⌋ , k ) \begin{aligned} &=g(n,\frac k p)-\sum_{i=1}^{\lfloor \frac n p\rfloor}\mu(i)\mu(p)[i\perp p][i\perp \frac k p]\\ &=g(n,\frac k p)+\sum_{i=1}^{\lfloor \frac n p\rfloor}\mu(i)[i\perp k]\\ &=g(n,\frac k p)+g(\lfloor \frac n p\rfloor,k) \end{aligned} =g(n,pk)i=1pnμ(i)μ(p)[ip][ipk]=g(n,pk)+i=1pnμ(i)[ik]=g(n,pk)+g(pn,k)

f , g f,g f,g 的个数是 n × log ⁡ k log ⁡ log ⁡ k \sqrt n\times \dfrac {\log k} {\log\log k} n ×loglogklogk 的,记忆化一下递推就好,当 k = 1 k=1 k=1 时, f ( n , k ) f(n,k) f(n,k) 显然为 n n n g ( n , k ) g(n,k) g(n,k) 就是 μ \mu μ 的前 n n n 项之和,弄一个杜教筛就好。

然而存 f , g f,g f,g 时我写的哈希冲突很多所以跑得飞慢,最后直接换了map反而来的快……代码如下:

#include <bits/stdc++.h>
using namespace std;
#define maxn 3000010

int n,m,k;
int prime[maxn],t=0,mu[maxn];
bool v[maxn];
void SieveInit(){
	mu[1]=1;
	for(int i=2;i<=maxn-10;i++){
		if(!v[i])prime[++t]=i,mu[i]=-1;
		for(int j=1;j<=t&&i*prime[j]<=maxn-10;j++){
			v[i*prime[j]]=true;
			if(i%prime[j]==0)break;
			mu[i*prime[j]]=-mu[i];
		}
		mu[i]+=mu[i-1];
	}
}
map<int,int> Smu;
struct par{
	int x,y;
	bool operator <(const par &B)const{return x==B.x?y<B.y:x<B.x;}
};
map<par,int> f,g;
int calc_mu(int x){
	if(x<=maxn-10)return mu[x];
	if(Smu.count(x))return Smu[x];
	long long tot=1;
	for(int l=2,r;l<=x;l=r+1){
		r=x/(x/l);
		tot-=calc_mu(x/l)*(r-l+1);
	}
	Smu[x]=tot;
	return tot;
}
int fac[maxn];
int calc_f(int x,int y){
	if(x==0)return 0;
	if(y==1)return x;
	par p=(par){x,y};int re;
	if(f.count(p))return f[p];
	re=calc_f(x,y/fac[y])-calc_f(x/fac[y],y/fac[y]);
	f[p]=re;
	return re;
}
int calc_g(int x,int y){
	if(x==0)return 0;
	if(y==1)return calc_mu(x);
	par p=(par){x,y};int re;
	if(g.count(p))return g[p];
	re=calc_g(x,y/fac[y])+calc_g(x/fac[y],y);
	g[p]=re;
	return re;
}

int main()
{
	scanf("%d %d %d",&n,&m,&k);SieveInit();
	for(int i=2;i<=k;i++)//去掉多余的质因子
		while(k%(i*i)==0)k/=i;
	int k_=k;for(int i=2;i<=k;i++)
		if(k_%i==0)fac[k_]=i,k_/=i;
	long long ans=0;
	for(int l=1,r;l<=min(n,m);l=r+1){
		r=min(n/(n/l),m/(m/l));
		ans+=1ll*(calc_g(r,k)-calc_g(l-1,k))*(n/l)*calc_f(m/l,k);
	}
	printf("%lld",ans);
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值