题目-破译密码

问题分析
给定 a , b , d a, b, d a,b,d, 问 0 ≤ x ≤ a 0 \le x \le a 0≤x≤a并且 0 ≤ y ≤ b 0 \le y \le b 0≤y≤b使得 gcd ( x , y ) = d \gcd (x, y) = d gcd(x,y)=d
问题等价于
0 ≤ x ≤ a d , 0 ≤ y ≤ b d , gcd ( x , y ) = 1 0 \le x \le \frac{a}{d}, 0 \le y \le \frac{b}{d}, \gcd(x, y) = 1 0≤x≤da,0≤y≤db,gcd(x,y)=1
满足上述条件 x , y x, y x,y互质的数对个数, 可以用容斥原理
M o ¨ b i u s Möbius Mo¨bius函数
假设对数字 x x x分解质因数的结果是 p 1 α 1 p 2 α 2 . . . p k α k p_1 ^ {\alpha_1}p_2^ {\alpha_2}...p_k ^ {\alpha_k} p1α1p2α2...pkαk
μ ( x ) = { 0 α i > 1 1 k m o d 2 = 0 , α i = 1 − 1 k m o d 2 = 1 , α i = 1 \mu (x) = \begin{cases} 0 \; \; \; \; \; \; \; \; \; \; \alpha_i > 1\\ 1 \; \; \; \; \; \; \; \; \; \; k \mod 2 = 0, \alpha_i = 1\\ -1 \; \; \; \; \; \; \; k \mod 2 = 1, \alpha_i = 1 \end{cases} μ(x)=⎩ ⎨ ⎧0αi>11kmod2=0,αi=1−1kmod2=1,αi=1
假设计算与 N N N互质的数的个数, S k S_k Sk是 1 ∼ n 1 \sim n 1∼n中 p p p的倍数的个数
c n t = N − S 2 − S 3 − . . . − S p + S 2 , 3 + S 2 , 5 + . . . − S 2 , 3 , 5 − . . . cnt = N - S_2 - S_3 - ... -S_{p} + S_{2, 3} + S_{2, 5} + ... - S_{2, 3, 5} - ... cnt=N−S2−S3−...−Sp+S2,3+S2,5+...−S2,3,5−...
发现符号就是 μ ( x ) \mu(x) μ(x)!, 可以在线性筛质数的情况下求出, 还是将情况分为三类
- 当前数字是质数, μ ( i ) = − 1 \mu(i) = -1 μ(i)=−1
- i i i不是 p j p_j pj的最小质因子, μ ( i × p j ) = − 1 × μ ( i ) \mu(i \times p_j) = -1 \times \mu(i) μ(i×pj)=−1×μ(i)
- i i i是 p j p_j pj的最小质因子, μ ( i × p j ) = 0 \mu(i \times p_j) = 0 μ(i×pj)=0
令 a ′ = a d a' = \frac{a}{d} a′=da. b ′ = b d b' = \frac{b}{d} b′=db, 实际的数量就是
a ′ b ′ − S 2 − S 3 − . . . + S 2 , 3 + S 2 , 5 + . . . − S 2 , 3 , 5 − . . . a'b' - S_2 - S_3 - ... + S_{2, 3} + S_{2, 5} + ... - S_{2, 3, 5} - ... a′b′−S2−S3−...+S2,3+S2,5+...−S2,3,5−...
结合 M o ¨ b i u s Möbius Mo¨bius函数, 得到
a ′ b ′ + ∑ i = 2 min ( a ′ , b ′ ) ⌊ a ′ i ⌋ ⌊ b ′ i ⌋ × μ ( i ) a'b' + \sum_{i = 2} ^ {\min(a', b')} \left \lfloor \frac{a'}{i} \right \rfloor \left \lfloor \frac{b'}{i}\right \rfloor \times \mu(i) a′b′+i=2∑min(a′,b′)⌊ia′⌋⌊ib′⌋×μ(i)
现在令 a = a ′ a = a' a=a′, 展开得到
a 1 , a 2 , a 3 , . . . . , a a \frac{a}{1}, \frac{a}{2}, \frac{a}{3}, ...., \frac{a}{a} 1a,2a,3a,....,aa
引理: 上述数列不同数的数字个数不会超过 2 n 2 \sqrt n 2n
证明:
将序列分为两部分, 第一部分有 a \sqrt a a项
a 1 , a 2 , . . . , a ⌊ a ⌋ \frac{a}{1}, \frac{a}{2}, ..., \frac{a}{\left \lfloor \sqrt a \right \rfloor } 1a,2a,...,⌊a⌋a
第二部分是单调递减的, 取值最大不会超过 a \sqrt a a
a ⌊ a ⌋ + 1 \frac{a}{\left \lfloor \sqrt a \right \rfloor + 1} ⌊a⌋+1a
因此第二部分不同的取值最多有 a \sqrt a a个, 因此第二段最多 a \sqrt a a项, 因此上述数列不同数的数字个数不会超过 2 n 2 \sqrt n 2n, 证毕

在计算段内的时候, 因为数值 a ′ i \frac{a'}{i} ia′是相同的, 但是 μ ( x ) \mu (x) μ(x)不是相同的, 可以预处理 μ ( x ) \mu (x) μ(x)的前缀和, 然后再累计段内的值
分段的正确性已经证明, 现在问题变成了如何进行分段?
假设当前情况是
⌊ a x ⌋ \left \lfloor \frac{a}{x} \right \rfloor ⌊xa⌋
希望计算 x x x最大到多少, 我们假设是 g ( x ) g(x) g(x), 使得下面的式子成立
⌊ a x ⌋ = ⌊ a g ( x ) ⌋ , ⌊ a g ( x ) + 1 ⌋ < ⌊ a x ⌋ \left \lfloor \frac{a}{x} \right \rfloor = \left \lfloor \frac{a}{g(x)} \right \rfloor, \; \; \; \; \left \lfloor \frac{a}{g(x) + 1} \right \rfloor < \left \lfloor \frac{a}{x} \right \rfloor ⌊xa⌋=⌊g(x)a⌋,⌊g(x)+1a⌋<⌊xa⌋
假设
g ( x ) = ⌊ a ⌊ a x ⌋ ⌋ g(x) = \left \lfloor \frac{a}{\left \lfloor \frac{a}{x} \right \rfloor } \right \rfloor g(x)=⌊⌊xa⌋a⌋
现在证明 g ( x ) g(x) g(x)的正确性
首先证明 ⌊ a x ⌋ = ⌊ a g ( x ) ⌋ \left \lfloor \frac{a}{x} \right \rfloor = \left \lfloor \frac{a}{g(x)} \right \rfloor ⌊xa⌋=⌊g(x)a⌋
证明:
假设将 g ( x ) g(x) g(x)内部的下取整符号去掉, g ( x ) ′ = x g(x)' = x g(x)′=x, 因为去掉下取整 g ( x ) g(x) g(x)会变大, 也就是 g ( x ) ≥ g ( x ) ′ g(x) \ge g(x)' g(x)≥g(x)′, 得到 g ( x ) ≥ x g(x) \ge x g(x)≥x, 那么得到
⌊ a x ⌋ ≥ ⌊ a g ( x ) ⌋ ⋯ ( 1 ) \left \lfloor \frac{a}{x} \right \rfloor \ge \left \lfloor \frac{a}{g(x)} \right \rfloor \; \; \; \; \; \; \cdots (1) ⌊xa⌋≥⌊g(x)a⌋⋯(1)
现在需要证明的是
⌊ a x ⌋ ≤ ⌊ a g ( x ) ⌋ \left \lfloor \frac{a}{x} \right \rfloor \le \left \lfloor \frac{a}{g(x)} \right \rfloor ⌊xa⌋≤⌊g(x)a⌋
将式子展开
⌊ a x ⌋ ≤ ⌊ a ⌊ a ⌊ a x ⌋ ⌋ ⌋ \left \lfloor \frac{a}{x} \right \rfloor \le \left \lfloor \frac{a}{ \left \lfloor \frac{a}{\left \lfloor \frac{a}{x} \right \rfloor } \right \rfloor } \right \rfloor ⌊xa⌋≤ ⌊⌊xa⌋a⌋a
令
t = ⌊ a a ⌊ a x ⌋ ⌋ t = \left \lfloor \frac{a}{ \frac{a}{\left \lfloor \frac{a}{x} \right \rfloor } } \right \rfloor t= ⌊xa⌋aa
那么 t = ⌊ a x ⌋ t = \left \lfloor \frac{a}{x} \right \rfloor t=⌊xa⌋, 也就是说右边式子去掉一个下取整符号等于左边, 也就是原来的数变小等于左边, 那么右边大于等于左边
⌊ a x ⌋ ≤ ⌊ a g ( x ) ⌋ ⋯ ( 2 ) \left \lfloor \frac{a}{x} \right \rfloor \le \left \lfloor \frac{a}{g(x)} \right \rfloor \; \; \; \; \; \; \cdots (2) ⌊xa⌋≤⌊g(x)a⌋⋯(2)
由 ( 1 ) (1) (1)和 ( 2 ) (2) (2), 证明出 ⌊ a x ⌋ = ⌊ a g ( x ) ⌋ \left \lfloor \frac{a}{x} \right \rfloor = \left \lfloor \frac{a}{g(x)} \right \rfloor ⌊xa⌋=⌊g(x)a⌋, 证毕
再证明 ⌊ a g ( x ) + 1 ⌋ < ⌊ a x ⌋ \left \lfloor \frac{a}{g(x) + 1} \right \rfloor < \left \lfloor \frac{a}{x} \right \rfloor ⌊g(x)+1a⌋<⌊xa⌋
证明:
设 a = k x + r , ( 0 ≤ r < x ) a = kx + r, (0 \le r < x) a=kx+r,(0≤r<x), 待入式子中得到
k > ⌊ a ⌊ a k ⌋ + 1 ⌋ k > \left \lfloor \frac{a}{\left \lfloor \frac{a}{k} \right \rfloor + 1} \right \rfloor k>⌊⌊ka⌋+1a⌋
移项得到
k ⋅ ( ⌊ a k ⌋ + 1 ) > a k \cdot (\left \lfloor \frac{a}{k} \right \rfloor + 1) > a k⋅(⌊ka⌋+1)>a
判断是否成立, 令 a = p k + q , ( 0 ≤ q < k ) a = pk + q, (0 \le q < k) a=pk+q,(0≤q<k), 那么有
k ⋅ ( p + 1 ) > p k + q k \cdot (p + 1) > pk + q k⋅(p+1)>pk+q
化简得到
k > q k > q k>q
成立, 因此 ⌊ a g ( x ) + 1 ⌋ < ⌊ a x ⌋ \left \lfloor \frac{a}{g(x) + 1} \right \rfloor < \left \lfloor \frac{a}{x} \right \rfloor ⌊g(x)+1a⌋<⌊xa⌋, 证毕
g ( x ) g(x) g(x)就是 x x x能达到的最大值, 通过该函数, 能计算出段的边界
算法步骤
- 线性筛法求 M o ¨ b i u s Möbius Mo¨bius函数
- 求 M o ¨ b i u s Möbius Mo¨bius函数的前缀和
- 计算答案
经过数论分块算法之后, 整体的算法时间复杂度降低到 O ( n n ) O(n \sqrt n) O(nn)
代码实现
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 50010;
int T;
int a, b, d;
int primes[N], cnt;
bool st[N];
int mobius[N];
LL s[N];
void init(int n) {
mobius[1] = 1;
for (int i = 2; i <= n; ++i) {
if (!st[i]) {
primes[cnt++] = i;
mobius[i] = -1;
}
for (int j = 0; primes[j] <= n / i; ++j) {
st[i * primes[j]] = true;
if (i % primes[j] == 0) {
mobius[i * primes[j]] = 0;
break;
}
mobius[i * primes[j]] = -1 * mobius[i];
}
}
for (int i = 1; i <= n; ++i) s[i] = s[i - 1] + mobius[i];
}
int g(LL a, LL x) {
return a / (a / x);
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
init(N - 1);
cin >> T;
while (T--) {
cin >> a >> b >> d;
a = a / d, b = b / d;
LL ans = 0;
int n = min(a, b);
for (int l = 1, r; l <= n; l = r + 1) {
r = min({
n, g(a, l), g(b, l)});
ans += (s[r] - s[l - 1]) * (LL) (a / l) * (b / l);
}
cout << ans << '\n';
}
return 0;
}

京公网安备 11010502036488号