1 条题解

  • 0
    @ 2025-10-22 16:39:30

    「POI2019 R1」一对项链 Pair of necklaces 题解

    题目大意

    给定两个数组 a(长度 n)和 b(长度 m),要求找到最大的长度 L,使得:

    • a 中取长度为 L 的连续子数组
    • b 中取长度为 L 的连续子数组
    • 这两个子数组的元素和奇偶性相同

    算法思路

    关键观察

    1. 奇偶性只与元素模 2 有关
      将每个元素 % 2(0 表示偶数,1 表示奇数),不影响结果。

    2. 前缀和模 2 简化计算
      定义:

      pa[i] = (a[0] + a[1] + ... + a[i]) % 2
      pb[i] = (b[0] + b[1] + ... + b[i]) % 2
      

      区间 [i, i+L-1] 的和的奇偶性 = pa[i+L-1] ⊕ pa[i-1](⊕ 表示异或)

    3. 问题转化
      要找最大的 L 使得存在 i, j 满足:

      pa[i+L-1] ⊕ pa[i-1] == pb[j+L-1] ⊕ pb[j-1]
      

    核心思路

    对于每个可能的长度 L,我们需要检查:

    • a 中是否存在某个 i 使得区间和为偶数(即 pa[i+L-1] == pa[i-1]
    • a 中是否存在某个 i 使得区间和为奇数(即 pa[i+L-1] != pa[i-1]
    • b 同样检查

    如果 ab 在长度 L 上能提供的奇偶性有交集,则 L 可行。

    高效算法

    直接枚举所有 L 会超时,我们需要 O(n+m) 的算法。

    关键技巧

    • 预处理出对于每个 Lab 分别能提供哪些奇偶性
    • 由于 n, m ≤ 1e5,但总输入量可达 2e7,需要线性算法

    具体实现

    1. 对数组 a

      • 找出所有能提供偶数和的长度 L(即存在 i 使得 pa[i] == pa[i+L]
      • 找出所有能提供奇数和的长度 L(即存在 i 使得 pa[i] != pa[i+L]
    2. 对数组 b 同样处理

    3. 从大到小枚举 L,找到第一个 L 使得 ab 在该长度上的奇偶性集合有交集

    查找所有可行长度的高效方法

    对于数组 arr(即 papb),我们想要:

    • 所有满足 ∃i: arr[i] == arr[i+L]L
    • 所有满足 ∃i: arr[i] != arr[i+L]L

    这可以通过以下方法在线性时间内完成:

    • 记录每个值 (0/1) 最早出现的位置
    • 遍历数组,对于每个位置 i,考虑它与之前同值位置的距离
    • 同时维护最小不同值距离

    代码实现

    #include <bits/stdc++.h>
    using namespace std;
    
    const int MAX = 100005;
    
    int n, m;
    int a[MAX], b[MAX];
    int pa[MAX], pb[MAX];
    bool a_even[MAX], a_odd[MAX], b_even[MAX], b_odd[MAX];
    
    void compute_parity_ranges(int arr[], int len, bool even[], bool odd[]) {
        // 初始化
        for (int L = 1; L <= len; L++) {
            even[L] = odd[L] = false;
        }
        
        // 记录每个值最早出现的位置
        vector<int> first_occ(2, -1);
        // 记录不同值的最早出现位置
        vector<int> first_diff_occ(2, -1);
        
        for (int i = 0; i < len; i++) {
            int val = arr[i];
            
            // 检查相同值
            if (first_occ[val] != -1) {
                int L = i - first_occ[val];
                if (L <= len) even[L] = true;
            }
            
            // 检查不同值
            int other_val = 1 - val;
            if (first_diff_occ[other_val] != -1) {
                int L = i - first_diff_occ[other_val];
                if (L <= len) odd[L] = true;
            }
            
            // 更新最早出现位置
            if (first_occ[val] == -1) {
                first_occ[val] = i;
            }
            
            // 更新不同值的最早出现位置
            if (first_diff_occ[val] == -1) {
                first_diff_occ[val] = i;
            }
        }
        
        // 处理特殊情况:如果数组中有至少两个不同值,那么所有长度都能提供奇数和
        bool has_zero = false, has_one = false;
        for (int i = 0; i < len; i++) {
            if (arr[i] == 0) has_zero = true;
            else has_one = true;
        }
        if (has_zero && has_one) {
            for (int L = 1; L <= len; L++) {
                odd[L] = true;
            }
        }
        
        // 处理特殊情况:如果数组中有重复值,那么所有长度都能提供偶数和
        // 实际上我们已经在上面通过 first_occ 处理了
    }
    
    int solve() {
        cin >> n >> m;
        for (int i = 0; i < n; i++) {
            cin >> a[i];
            a[i] %= 2;
        }
        for (int i = 0; i < m; i++) {
            cin >> b[i];
            b[i] %= 2;
        }
    
        // 计算前缀和模2
        pa[0] = a[0];
        for (int i = 1; i < n; i++) pa[i] = (pa[i-1] + a[i]) % 2;
        pb[0] = b[0];
        for (int i = 1; i < m; i++) pb[i] = (pb[i-1] + b[i]) % 2;
    
        // 计算每个长度能提供的奇偶性
        compute_parity_ranges(pa, n, a_even, a_odd);
        compute_parity_ranges(pb, m, b_even, b_odd);
    
        // 从大到小找第一个可行的L
        int limit = min(n, m);
        for (int L = limit; L >= 1; L--) {
            if ((a_even[L] && b_even[L]) || (a_odd[L] && b_odd[L])) {
                return L;
            }
        }
        return 0;
    }
    
    int main() {
        ios_base::sync_with_stdio(false);
        cin.tie(NULL);
        int q;
        cin >> q;
        while (q--) cout << solve() << "\n";
        return 0;
    }
    

    算法正确性说明

    对于样例:

    n=6, m=4
    a = [0,1,2,3,4,5] → 模2后 [0,1,0,1,0,1]
    b = [3,1,3,6] → 模2后 [1,1,1,0]
    
    pa = [0,1,1,0,0,1]
    pb = [1,0,1,1]
    

    算法会正确找到:

    • 当 L=3 时,a 能提供偶数和与奇数和,b 也能提供偶数和与奇数和
    • 当 L=4 时,a 只能提供偶数和,b 只能提供奇数和,不满足条件

    因此输出 3。

    复杂度分析

    • 时间复杂度:O(n + m) 每个测试用例
    • 空间复杂度:O(n + m)

    总结

    本题的关键在于:

    1. 将区间和奇偶性问题转化为前缀和异或问题
    2. 预处理每个长度能提供的奇偶性集合
    3. 高效地找到最大的满足条件的长度

    这种方法充分利用了奇偶性的性质,通过线性预处理和线性扫描解决问题,能够处理大规模数据。

    • 1

    「POI2019 R1」一对项链 Pair of necklaces

    信息

    ID
    3669
    时间
    2000ms
    内存
    256MiB
    难度
    10
    标签
    递交数
    2
    已通过
    1
    上传者