题意

求解 1xn, 1ym 中满足在 k 进制下 xy 是有限循环小数的个数

题解

很好然后我一开始在计算器上敲了半个小时然后由于眼瞎并且归纳能力差并没有发现规律

xy 的循环节长度为 l,则有
xklyxkly=xyxyxklxklyy=xxyyxklx(mody)k1(mody)
也就是说只要满足 yk 即可满足题意,故可将问题化为
Ans=i=1nj=1m[(i,j)=1][(j,k)=1]=j=1m[(j,k)=1]d|jμ(d)nd=d=1nμ(d)ndd|j[(j,k)=1]jd,dk=d=1n[(d,k)=1]μ(d)nd(j=1md[(j,k)=1])
于是接下来变为处理
f1(n,k)=d=1n[(d,k)=1]μ(d)f2(n)=d=1n[(d,k)=1]
先说 f2(n),较好处理,即考虑长度为 k 的区间将 n 分段,其中除了最后多出来的那一段其余的段得到的贡献是相同的,即 φ(k),故
f2(n)=nkφ(k)+f2(n%k)
那么对于 f1(n,k),容易变换得
f1(n,k)=d|knμ(d)l=1ndμ(ld)
然后我就不会了,看了下题解,可以发现若 μ(ld) 有贡献,则需满足 ld,此时有 μ(ld)=μ(l)μ(d),故
f1(n,k)=d|kμ(d)l=1ndμ(l)μ(d)[(d,l)=1]=d|kμ2(d)l=1ndμ(l)[(d,l)=1]=d|kμ2(d)f1(nd,d)
很好我还是第一次写到莫反带递归的

注意边界条件 n=0k=1,对于 n=0 返回 0 即可,对于 k=1f1(n,1) 即为 d=1nμ(d),杜教筛一下即可

最后再整除分块一次求总答案就好了

代码

cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
#include <iostream>
#include <cstdio>
#include <cstring>
#include <tr1/unordered_map>
#include <map>

using namespace std;

typedef long long LL;

const int MAXM = 1e06 + 10;
const int MAXK = 2000 + 10;

const int MAX = 1e06;

int N, M, K;

int prime[MAXM / 10]= {0};
bool visit[MAXM]= {false};
int pcnt = 0;
int mu[MAXM]= {0}, sumu[MAXM]= {0};
void linear_sieve () {
sumu[1] = mu[1] = 1;
for (int i = 2; i <= MAX; i ++) {
if (! visit[i]) {
prime[++ pcnt] = i;
mu[i] = - 1;
}
for (int j = 1; j <= pcnt && i * prime[j] <= MAX; j ++) {
visit[i * prime[j]] = true;
if (! (i % prime[j])) break;
mu[i * prime[j]] = - mu[i];
}
sumu[i] = sumu[i - 1] + mu[i];
}
}

tr1::unordered_map<int, int> mapmu;
int mu_sieve (int n) {
if (n <= MAX) return sumu[n];
if (mapmu[n]) return mapmu[n];
int total = 1;
for (int l = 2, r; l <= n; l = r + 1) {
r = n / (n / l);
total -= (r - l + 1) * mu_sieve (n / l);
}
return mapmu[n] = total;
}
LL f[MAXK]= {0};
LL sumr[MAXM]= {0}; // sigma ([(j, k) = 1])
int GCD (int a, int b) {
return ! b ? a : GCD (b, a % b);
}
void r_sieve () {
for (int i = 1; i <= K; i ++)
f[i] = f[i - 1] + (GCD (i, K) == 1);
}
int rval (int n) {
return (n / K) * f[K] + f[n % K];
}

map<pair<int, int>, int> mapl;
LL l_solve (int n, int k) { // S(n, K)
if (! n) return 0;
pair<int, int> ret = make_pair (n, k);
if (mapl[ret]) return mapl[ret];
if (k == 1) return mu_sieve (n);
LL total = 0;
for (int i = 1; i * i <= k; i ++)
if (! (k % i)) {
if (mu[i]) total += l_solve (n / i, i);
if (i * i != k && mu[k / i])
total += l_solve (n / (k / i), k / i);
}
return mapl[ret] = total;
}

int main () {
linear_sieve ();
scanf ("%d%d%d", & N, & M, & K);
r_sieve ();
LL ans = 0, pre = 0;
for (int l = 1, r; l <= min (N, M); l = r + 1) {
r = min (N / (N / l), M / (M / l));
LL pl = l_solve (r, K);
ans += 1ll * (pl - pre) * (N / l) * rval (M / l);
pre = pl;
}
cout << ans << endl;

return 0;
}

/*
2 6 10
*/

/*
99 7689 100
ans: 257777
*/

方法二

我没有写这种方法,但是这种真的很简单的说
f(n,m,k)=i=1nj=1m[(i,j)=1][(j,k)=1]=i=1nj=1m[(i,j)=1]d|j,d|kμ(d)=d|kμ(d)i=1nd|jm[(i,j)=1]=d|kμ(d)i=1nj=1md[(i,jd)=1]=d|kμ(d)i=1nj=1md[(i,j)=1][(i,d)=1]=d|kμ(d)f(md,n,d)
边界 n=0 或者 m=0 返回 0k=1 返回 i=1nj=1m[(i,j)=1]=i=1min(n,m)μ(d)ndmd 即可

不过说实话这个虽然简单但是至少对于我不大容易想到,毕竟我会先拆 [(i,j)=1] 部分,因为总感觉 [(j,k)=1] 会多出一个 d|k 的限制条件

但是这个解法是真的厉害