1 条题解

  • 0
    @ 2026-5-17 17:20:04

    J. 可排序数组 详细题解

    问题重述

    给定数组 AA(长度 NN)和数组 XX(长度 MM)。对于每一对 (u,v)(u,v)1u<vM1\le u<v\le M),判断 AA 是否可以通过重新排列成为 (Xu,Xv)(X_u,X_v)-可排序的,即重排后对所有 i<ji<j 满足:

    $$A_i \oplus X_u \le A_j \oplus X_v \quad\text{和}\quad A_i \oplus X_v \le A_j \oplus X_u $$

    求满足条件的 (u,v)(u,v) 对数。


    核心观察

    1. 成对可排序性条件
      对于两个数 B0,B1B_0,B_1,考虑它们能否被“排序”使得同时满足上述两个不等式。通过逐位分析(从高位到低位)可得:
      P,QP,Q 为两个参数,则 (B0,B1)(B_0,B_1) 是可排序的当且仅当

      PQB0B1P \oplus Q \le B_0 \oplus B_1

      其中“可排序”指存在一种顺序(B0B_0 在前或 B1B_1 在前)使得两个不等式成立。

    2. 扩展到整个数组
      对整个数组 AA 而言,要存在一种重排使得所有元素对 (i,j)(i,j) 都满足条件,等价于要求 任意两个元素 Ai,AjA_i,A_j 都满足

      PQAiAjP \oplus Q \le A_i \oplus A_j

      因为最严格的限制来自异或值最小的那一对。令

      minXOR=mini<j(AiAj)\text{minXOR} = \min_{i<j} (A_i \oplus A_j)

      AA(P,Q)(P,Q)-可排序的当且仅当

      PQminXORP \oplus Q \le \text{minXOR}
    3. 充分性
      PQminXORP \oplus Q \le \text{minXOR} 时,可以通过将 AA 按照 Ai(P&Q)A_i \oplus (P \& Q) 的值排序,即可满足所有条件。因此该条件是充要的。


    算法步骤

    1. 计算 minXOR\text{minXOR}
      AA 排序,则最小异或值一定出现在相邻元素之间。计算 mini=1N1(AiAi+1)\min\limits_{i=1}^{N-1} (A_i \oplus A_{i+1}),复杂度 O(NlogN)O(N \log N)

    2. 统计满足 XuXvminXORX_u \oplus X_v \le \text{minXOR} 的无序对 (u,v)(u,v) 个数
      使用二进制 Trie 树(每个节点记录经过该节点的数值个数)。

      • 依次处理每个 XiX_i,在插入之前,查询 Trie 中已有数值与 XiX_i 异或值 minXOR\le \text{minXOR} 的个数。
      • 查询时按位从高到低,利用 minXOR\text{minXOR} 的二进制位控制分支:
        设当前位为 bb(从最高位到最低位),设 lim\text{lim}minXOR\text{minXOR} 的当前位。
        对于 XiX_i 的当前位 xx,在 Trie 中走与 xx 相同的方向时,异或值的当前位为 00,若 lim=1\text{lim}=1,则还需加上相反方向子树的所有值(因为异或值为 11 的路径若 lim=1\text{lim}=1 则后续任意均可)。
        具体实现时递归或迭代均可。
      • 查询后将 XiX_i 插入 Trie。
        最终累加得到的个数即为满足 XuXvminXORX_u \oplus X_v \le \text{minXOR} 的对数,即为答案。
    3. 复杂度

      • 排序 O(NlogN)O(N \log N)
      • Trie 操作 O(MlogmaxX)O(M30)O(M \cdot \log \max X) \le O(M \cdot 30),总复杂度 O(NlogN+MlogX)O(N \log N + M \log X),满足 N,M2×105N,M\le 2\times 10^5

    参考代码

    #include <bits/stdc++.h>
    using namespace std;
    using ll = long long;
    
    const int B = 30; // 因为 Ai, Xi < 2^30
    
    struct TrieNode {
        TrieNode* child[2];
        int cnt;
        TrieNode() : child{nullptr, nullptr}, cnt(0) {}
    };
    
    void insert(TrieNode* root, int x) {
        TrieNode* cur = root;
        for (int i = B-1; i >= 0; --i) {
            int b = (x >> i) & 1;
            if (!cur->child[b]) cur->child[b] = new TrieNode();
            cur = cur->child[b];
            cur->cnt++;
        }
    }
    
    int query(TrieNode* root, int x, int limit) {
        // 返回 trie 中与 x 异或值 <= limit 的数的个数
        TrieNode* cur = root;
        int ans = 0;
        for (int i = B-1; i >= 0; --i) {
            if (!cur) break;
            int xb = (x >> i) & 1;
            int lb = (limit >> i) & 1;
            if (lb == 1) {
                // 当前位异或值为 0 的路径全部符合(因为后续任意都小于 limit)
                if (cur->child[xb]) ans += cur->child[xb]->cnt;
                // 走异或值为 1 的路径继续判断
                cur = cur->child[xb ^ 1];
            } else {
                // lb == 0,只能走异或值为 0 的路径
                cur = cur->child[xb];
            }
        }
        if (cur) ans += cur->cnt; // 完全相等的情况
        return ans;
    }
    
    int main() {
        ios::sync_with_stdio(false);
        cin.tie(nullptr);
        int N, M;
        cin >> N >> M;
        vector<int> A(N);
        for (int i = 0; i < N; ++i) cin >> A[i];
        vector<int> X(M);
        for (int i = 0; i < M; ++i) cin >> X[i];
    
        // 计算 minXOR
        sort(A.begin(), A.end());
        int minXOR = INT_MAX;
        for (int i = 0; i < N-1; ++i) {
            minXOR = min(minXOR, A[i] ^ A[i+1]);
        }
    
        // 统计满足 Xi ^ Xj <= minXOR 的无序对
        TrieNode* root = new TrieNode();
        ll ans = 0;
        for (int i = 0; i < M; ++i) {
            ans += query(root, X[i], minXOR);
            insert(root, X[i]);
        }
        cout << ans << '\n';
        return 0;
    }
    
    • 1

    信息

    ID
    7172
    时间
    1000ms
    内存
    256MiB
    难度
    5
    标签
    递交数
    0
    已通过
    0
    上传者