[NOI2016]循环之美

题意

牛牛是一个热爱算法设计的高中生。在他设计的算法中,常常会使用带小数的数进行计算。牛牛认为,如果在 (k) 进制下,一个数的小数部分是纯循环的,那么它就是美的。现在,牛牛想知道:对于已知的十进制数 (n)(m),在 (k) 进制下,有多少个数值上互不相等的纯循环小数,可以用分数 (frac xy) 表示,其中 (1≤x≤n,1≤y≤m),且 (x,y)是整数。一个数是纯循环的,当且仅当其可以写成以下形式:

(a.dot{c_1} c_2 c_3 dots c_{p - 1} dot{c_p})

其中,(a) 是一个整数,(p≥1);对于 (1≤i≤p)(c_i)(k) 进制下的一位数字。

例如,在十进制下,(0.45454545……=0.dot {4} dot {5})是纯循环的,它可以用 (frac {5}{11})(frac{10}{22}) 等分数表示;在十进制下,(0.1666666……=0.1dot6)则不是纯循环的,它可以用 (1/6) 等分数表示。需要特别注意的是,我们认为一个整数是纯循环的,因为它的小数部分可以表示成 (0) 的循环或是 (k-1) 的循环;而一个小数部分非 (0) 的有限小数不是纯循环的。

输入输出格式

输入格式:

只有一行,包含三个十进制数N,M,K意义如题所述,保证 1≤n≤109,1≤m≤109,2≤k≤2000

输出格式:

一行一个整数,表示满足条件的美的数的个数。

输入输出样例

输入样例#1:

2 6 10

输出样例#1:

4

题解

自闭==
好难啊,推了半天实在不会推式子的前半部分的那些东西
题目就是让求(sum_{i=1}^{n}sum_{j=1}^{m}{[gcd(i,j)=1][gcd(j,k)=1]})
(sum_{i=1}^{n}sum_{j=1}^{m}{[gcd(i,j)=1][gcd(j,k)=1]})
先考虑前面的(sum_{i=1}^{n}sum_{j=1}^{m}{[gcd(i,j)=1]})
很显然是(sum_{d=1}^{n}{mu(d)frac{n}{d}frac{m}{d}})
那现在式子就成了(sum_{d=1}^{n}{mu(d)frac{n}{d}}sum_{i=1}^{m/d}{[gcd(id,k)=1]})
这样的话(d)就必须满足(gcd(d,k)=1)
所以(sum_{d=1}^{n}{[gcd(d,k)=1]mu(d)frac{n}{d}}sum_{i=1}^{m/d}{[gcd(i,k)=1]})
先看后面的那一部分:
如果(frac{m}{d} | k)
那么答案就是(frac{m}{dk}phi(k))
对于剩下的那(frac{m}{d} \% k)的部分
我们暴力预处理出来就行了
所以后面的部分等于(frac{m}{dk}phi(k)+F(frac{m}{d} \% k))
这么一看我们只需要再求出一个(S(n,k)=sum_{d=1}^{n}{[gcd(d,k)=1]mu(d)})就行了
但是由于有一个限制,所以并不好筛
(sum_{d=1}^{n}{[gcd(d,k)=1]mu(d)}\=sum_{d=1}^{n}{mu(d)}sum_{t|gcd(d,k)}{mu(t)}\=sum_{d=1}^{n}{mu(d)}sum_{t|d,t|k}{mu(t)})
由于需要(t|d,t|k),所以(t<=k),所以满足条件的(d)一定是(d|k)
(\=sum_{d|k}{mu(d)}sum_{t|d}{mu(t)})
(=sum_{d|k}{mu(d)}sum_{t=1}^{n/d}{mu(dt)})
(mu(dt))不为0的话必须要满足(gcd(d,t)=1)
(=sum_{d|k}{mu(d)}sum_{t=1,gcd(d,t)=1}^{n/d}{mu(d)mu(t)}\=sum_{d|k}{mu(d)^2}sum_{t=1,gcd(d,t)=1}^{n/d}{mu(t)}\=sum_{d|k}{mu(d)^2}S(frac{n}{d},d))
这样就可以杜教筛+记搜了
只有(d=1)的时候才计算(summu),否则就枚举因数继续计算

代码

#include<map>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
# define MP(x , y) (make_pair(x , y))
# define LL long long
const int M = 4000005 ;

using namespace std ;

bool notp[M] ;
int p[M] , pnum , mu[M] ;
LL n , m , k , upp = 4000000 ;
LL sum[M] , f[M] , ans ;
map < pair < LL , LL > , LL > pi ;
inline int gcd(int a , int b) {
	if(b == 0) return a ;
	return gcd(b , a % b) ;
}
inline void presolve(int n) {
	sum[1] = 1 ;
	for(int i = 2 ; i <= n ; i ++) {
		if(!notp[i]) {
			p[++pnum] = i ;
			sum[i] = -1 ;
		}
		for(int j = 1 ; j <= pnum && 1LL * i * p[j] <= n ; j ++) {
			notp[i * p[j]] = true ;
			if(i % p[j] == 0) {
				sum[i * p[j]] = 0 ;
				break ;
			}
			else sum[i * p[j]] = - sum[i] ;
		}
	}
	for(int i = 1 ; i <= n ; i ++) {
		mu[i] = sum[i] ;
		sum[i] = sum[i - 1] + sum[i] ;
	}
	for(int i = 1 ; i <= k ; i ++) 
		f[i] = f[i - 1] + (gcd(i , k) == 1) ;
}

inline LL F(LL n) { 
	return f[k] * (n / k) + f[n % k] ;
}

inline LL Sum(LL n , LL k) {
	if(n == 0) return 0 ;
	if(k == 1 && n <= upp) return sum[n] ;
	if(pi[MP(n , k)]) return pi[MP(n , k)] ;
	LL ret = 0 ;
	if(k == 1) {
		ret = 1 ;
		for(LL l = 2 , r ; l <= n ; l = r + 1) {
			r = n / (n / l) ;
			ret -= (r - l + 1) * Sum(n / l , k) ;
		}
	}
	else {
		for(int x = 1 , y ; x * x <= k ; x ++) {
			if(k % x) continue ;
			if(mu[x]) 
				ret += Sum(n / x , x) ;
			y = k / x ;
			if(x == y) continue ;
			if(mu[y]) 
				ret += Sum(n / y , y) ;
		}
	}
	pi[MP(n , k)] = ret ;
	return ret ;
}
int main() {
	scanf("%lld%lld%lld",&n,&m,&k) ;
	presolve(4000000) ;
	for(LL l = 1 , r ; l <= min(n , m) ; l = r + 1) {
		r = min(n / (n / l) , m / (m / l)) ;
		ans += (Sum(r , k) - Sum(l - 1 , k)) * (n / l) * F(m / l) ;
	}
	printf("%lld
",ans) ;
	return 0 ;
}
原文地址:https://www.cnblogs.com/beretty/p/10435010.html