[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.1\dot6\)则不是纯循环的,它可以用 \(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\)的时候才计算\(\sum\mu\),否则就枚举因数继续计算
代码
#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\n",ans) ;
return 0 ;
}