2025“钉耙编程”中国大学生算法设计春季联赛(4)1003 洞察

题目大意

给定正整数 $ k, b, c, v $,求满足以下条件的非负整数 $ x $ 的数量:

\[x \oplus c \leq v \]

其中 $ \oplus $ 表示异或运算。输入包含多组测试数据,每组数据的参数范围极大($ 10^{18} $ 级别)。


解题思路

核心思想:二进制位逐位分析

由于异或运算的性质,直接比较二进制位的大小关系。从最高位到最低位逐位确定 $ x $ 的取值范围,通过位运算将问题分解为多个独立的子问题。


关键步骤

1. 预处理函数

定义函数 get(l, r) 计算满足区间 $ [l, r] $ 内合法 $ x $ 的数量。其数学表达式为:

\[\text{get}(l, r) = \sum_{x=l}^{r} \mathbb{I}[x \bmod k = b] \]

此函数用于快速统计区间内的合法 $ x $ 数量。


2. 逐位枚举

从最高位(62位)到最低位(0位)逐位处理:

  • 对于当前位 $ i $:
    • 若 $ v $ 的第 $ i $ 位为 $ 1 $:
      • 当前位异或结果为 $ 0 $:此时 $ x $ 的取值范围受限于该位的约束。
      • 当前位异或结果为 $ 1 $:此时该位的贡献可直接计入答案,后续位无需考虑。
    • 若 $ v $ 的第 $ i $ 位为 $ 0 $:
      • 当前位异或结果必须为 $ 0 $,否则直接返回无解。

3. 动态维护区间

使用变量 $ \text{now} $ 记录当前已确定的高位部分的异或结果。通过调整 $ \text{now} $ 的值,逐步缩小 $ x $ 的可能范围,并累加符合条件的 $ x $ 数量。


4. 特殊边界处理

最终需检查是否存在 $ x $ 使得 $ x \oplus c = v $,若存在则补全计数。


代码实现

以下是完整的代码实现:

#include<bits/stdc++.h>
#define int long long
using namespace std;

const int N = 1e5 + 10;

void solve() {
    int k, b, c, v;
    cin >> k >> b >> c >> v;
    
    // 定义函数 p(x),计算满足 (x - b) % k == 0 的最大 x 值
    auto p = [&](int x) {
        if (x < b) return 0ll;
        else if (x == b) return 1ll;
        else return (x - b) / k + 1ll;
    };
    
    int mx = 0;
    // 定义函数 get(l, r),计算区间 [l, r] 内合法 x 的数量
    auto get = [&](int l, int r) {
        mx = max(mx, p(r));
        return (p(r) - p(l - 1));
    };
    
    int now = 0, cnt = 0;
    // 逐位处理,从最高位到最低位
    for (int i = 62; i >= 0; i--) {
        if (v >> i & 1) { // 如果 v 的第 i 位为 1
            if (c >> i & 1) { // 如果 c 的第 i 位为 1
                now += (1ll << i); // 尝试设置当前位为 1
                cnt += get(now, now + (1ll << i) - 1); // 统计当前分支的贡献
                now -= (1ll << i); // 恢复状态
            } else { // 如果 c 的第 i 位为 0
                cnt += get(now, now + ((1ll << i) - 1)); // 直接统计当前分支的贡献
                now += (1ll << i); // 设置当前位为 1
            }
        } else if (c >> i & 1) { // 如果 v 的第 i 位为 0 且 c 的第 i 位为 1
            now += (1ll << i); // 必须设置当前位为 1
        }
    }
    
    // 最后检查是否存在恰好等于的情况
    now -= b;
    if (now >= 0 && now % k == 0) cnt++;
    cout << cnt << '\n';
}

signed main() {
    ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
    int T;
    cin >> T;
    while (T--) solve();
    return 0;
}

代码解析

1. 函数 p(x)

计算满足 $ x \bmod k = b $ 的最大 $ x $ 值:

\[p(x) = \begin{cases} 0 & \text{if } x < b \\ 1 & \text{if } x = b \\ \lfloor \frac{x - b}{k} \rfloor + 1 & \text{otherwise} \end{cases} \]


2. 函数 get(l, r)

利用 $ p(x) $ 快速统计区间 $ [l, r] $ 内的合法 $ x $ 数量:

\[\text{get}(l, r) = p(r) - p(l - 1) \]


3. 逐位处理

通过位运算判断当前位的可能取值,动态调整 $ \text{now} $ 并累加答案。


4. 边界处理

最后检查是否存在恰好等于 $ x \oplus c = v $ 的情况,避免遗漏。


时间复杂度

算法的时间复杂度为:

\[O(T \cdot \log v) \]

其中 $ T $ 是测试数据组数,$ \log v $ 是逐位处理的最大迭代次数。对于 $ v \leq 10^{18} $,该算法可以轻松通过所有测试数据。


posted @ 2025-03-29 03:14  archer2333  阅读(202)  评论(0)    收藏  举报