E. Serval and Modulo

Serval and Modulo 题解:模运算 + 因数分解

题目链接


题目分析

Serval 给定了两个数组 $ a $ 和 $ b $,其中 $ b $ 是通过将 $ a $ 中的每个元素对某个魔法数 $ k $ 取模后得到的,并且 $ b $ 被打乱了。我们需要找到一个可能的魔法数 $ k $(满足 $ 1 \leq k \leq 10^9 $),使得 $ b $ 可以由 $ a $ 按照上述规则生成。如果不存在这样的 $ k $,输出 $ -1 $。


思路大意

这道题的核心目标是找到一个合适的 $ k $,使得:

  1. 对于数组 $ a $ 的每个元素 $ a_i $,有 $ a_i \mod k = b_j $,其中 $ b_j $ 是数组 $ b $ 中的一个元素。
  2. 数组 $ b $ 是通过重新排列 $ [a_1 \mod k, a_2 \mod k, \dots, a_n \mod k] $ 得到的。

核心观察

  1. 如果数组 $ a $ 和 $ b $ 完全相同(即排序后相等),那么任意一个大于等于最大值的 $ k $ 都是合法答案。
  2. 如果数组 $ a $ 和 $ b $ 不同,则需要通过模运算找到一个合适的 $ k $。
  3. 关键在于利用 $ \text{sum}(a) - \text{sum}(b) $ 的差值 $ \text{diff} $ 来约束 $ k $ 的可能取值。

数学推导

假设 $ k $ 是一个合法的答案,则对于所有 $ i $,有:

\[a_i \mod k = b_j \]

这意味着 $ a_i - b_j $ 必须是 $ k $ 的倍数。因此,我们可以得出:

\[k \mid (a_i - b_j) \]

进一步地,如果我们对所有 $ i $ 计算 $ a_i - b_j $ 的和 $ \text{diff} = \text{sum}(a) - \text{sum}(b) $,则 $ k $ 必须是 $ \text{diff} $ 的因数。


算法设计

基于上述分析,我们可以设计如下算法:

  1. 输入与初始化:读入数组 $ a $ 和 $ b $,并计算两者的差值 $ \text{diff} = \text{sum}(a) - \text{sum}(b) $。
  2. 排序比较:对数组 $ a $ 和 $ b $ 排序。如果排序后的 $ a $ 和 $ b $ 相同,则直接输出一个较大的 $ k $(如 $ 10^6 + 1 $)。
  3. 检查差值:如果 $ \text{diff} \leq 0 $,则输出 $ -1 $。
  4. 枚举因数:枚举 $ \text{diff} $ 的所有因数 $ k $,并检查是否满足条件:
    • 将数组 $ a $ 中的每个元素对 $ k $ 取模,得到新数组 $ c $。
    • 排序 $ c $ 并与 $ b $ 比较,如果相等,则 $ k $ 是合法答案。
  5. 返回结果:如果找到合法的 $ k $,输出它;否则输出 $ -1 $。

代码详解

以下是完整的代码实现及其详细解释:

#include <bits/stdc++.h>
#define int long long
#define all(x) x.begin(),x.end()
#define rall(x) x.rbegin(),x.rend()
#define pb push_back
#define pii pair<int,int>
using namespace std;
const int mod = 998244353;

// 计算 gcd 的函数
int gcd(int a, int b) { return b ? gcd(b, a % b) : a; }

// 快速幂函数
int qpw(int a, int b) {
    int ans = 1;
    while (b) {
        if (b & 1) ans = ans * a % mod;
        a = a * a % mod;
        b >>= 1;
    }
    return ans;
}

// 求逆元函数
int inv(int x) { return qpw(x, mod - 2); }

void solve() {
    int n; cin >> n; // 输入数组长度
    vector<int> a(n), b(n);
    int dif = 0;

    // 输入数组 a 并计算 sum(a)
    for (int i = 0; i < n; i++) {
        cin >> a[i];
        dif += a[i];
    }

    // 输入数组 b 并计算 sum(b)
    for (int i = 0; i < n; i++) {
        cin >> b[i];
        dif -= b[i];
    }

    // 对数组 a 和 b 排序
    sort(all(a));
    sort(all(b));

    // 如果 a 和 b 相同,直接输出一个较大的 k
    if (a == b) {
        cout << (int)(1e6 + 1) << '\n';
        return;
    }

    // 如果 diff <= 0,说明找不到合法的 k
    if (dif <= 0) {
        cout << -1 << '\n';
        return;
    }

    // 定义 check 函数,验证 k 是否合法
    auto check = [&](int x) {
        vector<int> c(n);
        for (int i = 0; i < n; i++) {
            c[i] = a[i] % x;
        }
        sort(all(c));
        return c == b;
    };

    // 枚举 diff 的因数
    for (int i = 1; i * i <= dif; i++) {
        if (dif % i == 0) {
            if (check(i)) {
                cout << i << '\n';
                return;
            }
            if (check(dif / i)) {
                cout << dif / i << '\n';
                return;
            }
        }
    }

    // 如果没有找到合法的 k,输出 -1
    cout << -1 << '\n';
}

signed main() {
    ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr); // 加速输入输出
    int _ = 1; cin >> _; // 测试用例数量
    while (_--) solve(); // 处理每个测试用例
}

代码关键部分解析

sort

  • 作用:对数组进行排序。
  • 特性:在本题中,我们使用排序来判断数组 $ a $ 和 $ b $ 是否可以直接匹配。

差值计算

for (int i = 0; i < n; i++) {
    cin >> a[i];
    dif += a[i];
}
for (int i = 0; i < n; i++) {
    cin >> b[i];
    dif -= b[i];
}
  • 作用:计算数组 $ a $ 和 $ b $ 的差值 $ \text{diff} $。
  • 特性:如果 $ \text{diff} \leq 0 $,则说明找不到合法的 $ k $。

枚举因数

for (int i = 1; i * i <= dif; i++) {
    if (dif % i == 0) {
        if (check(i)) {
            cout << i << '\n';
            return;
        }
        if (check(dif / i)) {
            cout << dif / i << '\n';
            return;
        }
    }
}
  • 作用:枚举 $ \text{diff} $ 的所有因数,并检查是否满足条件。
  • 特性:通过因数分解减少枚举范围,提高效率。

检查函数

auto check = [&](int x) {
    vector<int> c(n);
    for (int i = 0; i < n; i++) {
        c[i] = a[i] % x;
    }
    sort(all(c));
    return c == b;
};
  • 作用:验证给定的 $ k $ 是否合法。
  • 特性:通过对数组 $ a $ 的每个元素取模并排序,判断是否与数组 $ b $ 相同。

示例分析

输入

5
4
3 5 2 7
0 1 1 1
5
3 1 5 2 4
1 2 3 4 5
6
2 3 4 7 8 9
1 2 3 6 7 8
5
21 22 25 28 20
0 1 2 1 0
6
1 1 2 3 5 8
0 0 1 1 0 0

输出

2
31415926
-1
4
-1

解释

  1. 第一个测试用例

    • $ a = [3, 5, 2, 7] \(,\) b = [0, 1, 1, 1] $。
    • $ \text{diff} = 17 - 2 = 15 $。
    • 枚举 $ \text{diff} $ 的因数 $ 1, 3, 5, 15 $,发现 $ k = 2 $ 合法。
    • 输出 $ 2 $。
  2. 第二个测试用例

    • $ a = [3, 1, 5, 2, 4] \(,\) b = [1, 2, 3, 4, 5] $。
    • $ \text{diff} = 15 - 15 = 0 $。
    • 输出 $ 31415926 $(任意一个大于等于最大值的 $ k $)。
  3. 第三个测试用例

    • $ a = [2, 3, 4, 7, 8, 9] \(,\) b = [1, 2, 3, 6, 7, 8] $。
    • 无法找到合法的 $ k $。
    • 输出 $ -1 $。

posted @ 2025-03-24 18:43  archer2333  阅读(31)  评论(0)    收藏  举报