题目-破译密码

在这里插入图片描述

问题分析

给定 a , b , d a, b, d a,b,d, 问 0 ≤ x ≤ a 0 \le x \le a 0xa并且 0 ≤ y ≤ b 0 \le y \le b 0yb使得 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 0xda,0ydb,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=11kmod2=1,αi=1

假设计算与 N N N互质的数的个数, S k S_k Sk 1 ∼ n 1 \sim n 1n 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=NS2S3...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} - ... abS2S3...+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) ab+i=2min(a,b)iaib×μ(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)=xaa
现在证明 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) xag(x)a(1)
现在需要证明的是
⌊ a x ⌋ ≤ ⌊ a g ( x ) ⌋ \left \lfloor \frac{a}{x} \right \rfloor \le \left \lfloor \frac{a}{g(x)} \right \rfloor xag(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 xaaa

t = ⌊ a a ⌊ a x ⌋ ⌋ t = \left \lfloor \frac{a}{ \frac{a}{\left \lfloor \frac{a}{x} \right \rfloor } } \right \rfloor t= xaaa
那么 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) xag(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,(0r<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,(0q<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;
}