P15445永远在一起!题解(月赛T2)

P15445 「IXOI R1」永远在一起! 题解

题目传送门P15445

题目大意

给定 Q Q Q 次询问,每次给出三个整数 x , y , z x, y, z x,y,z。需要判断是否存在两个不相等的自然数 a , b a, b a,b 满足:

  1. a ∣ b = x a \mid b = x a∣b=x(按位或运算)
  2. ∣ a − b ∣ ≤ y |a - b| \leq y ∣a−b∣≤y
  3. a + b ≤ z a + b \leq z a+b≤z

若存在输出 YES,否则输出 NO

解题思路

核心观察

  1. 特判边界

    • 若 x > z x > z x>z,无解( a + b ≥ x a+b \geq x a+b≥x 恒成立)
    • 若 y ≥ x y \geq x y≥x,取 ( a , b ) = ( x , 0 ) (a,b)=(x,0) (a,b)=(x,0) 即满足条件
    • 设 l o w = x & − x low = x \& -x low=x&−x( x x x 最低位的 1 对应的值),若 y < l o w y < low y<low 则无解(任意解 ∣ a − b ∣ ≥ l o w |a-b| \geq low ∣a−b∣≥low)
  2. 二进制拆分特性

    • 将 x x x 的二进制位分解后,计算交替相减的最小绝对值 d m i n dmin dmin(如 x = 5 x=5 x=5 时 d m i n = ∣ 4 − 1 ∣ = 3 dmin=|4-1|=3 dmin=∣4−1∣=3)
    • 若 d m i n ≤ y dmin \leq y dmin≤y,可通过正负号分配构造解
  3. 子集构造法

    • 寻找 c c c 满足 c ⊆ x c \subseteq x c⊆x(二进制子集)且 c ∈ [ max ⁡ ( 0 , x − y ) , z − x ] c \in [\max(0,x-y), z-x] c∈[max(0,x−y),z−x]
    • 构造 ( a , b ) = ( x , x − c ) (a,b)=(x, x-c) (a,b)=(x,x−c) 满足条件(需 c ≠ x c \neq x c=x)
  4. DFS 位分配

    • 将 x x x 的二进制位从高到低分解
    • 对每个二进制位选择分配方案:
      • 同时分配给 a a a 和 b b b(加入公共和 s u m c sum_c sumc)
      • 只分配给 a a a(增加差值 + v a l +val +val)
      • 只分配给 b b b(减少差值 − v a l -val −val)
    • 约束条件: s u m c ≤ z − x sum_c \leq z - x sumc≤z−x 且 ∣ Δ ∣ ≤ y |\Delta| \leq y ∣Δ∣≤y 且 Δ ≠ 0 \Delta \neq 0 Δ=0

算法流程

  1. 特判 x > z x>z x>z 直接返回 NO
  2. 特判 y ≥ x y \geq x y≥x 直接返回 YES
  3. 计算 l o w = x & − x low = x \& -x low=x&−x,若 y < l o w y < low y<low 返回 NO
  4. 计算二进制位交替相减的 d m i n dmin dmin,若 d m i n ≤ y dmin \leq y dmin≤y 返回 YES
  5. 在区间 [ max ⁡ ( 0 , x − y ) , z − x ] [\max(0,x-y), z-x] [max(0,x−y),z−x] 中寻找满足 c ⊆ x c \subseteq x c⊆x 且 c ≠ x c \neq x c=x 的值
  6. 若步骤 5 失败,使用 DFS 进行二进制位分配搜索
  7. 根据搜索结果输出 YESNO

正确性证明

  1. 边界特判覆盖了无解和易解情况
  2. 子集构造法保证 a ∣ b = x a \mid b=x a∣b=x 且满足差值约束
  3. DFS 分配模拟所有可能的位分配方案,剪枝策略保证效率:
    • 当前 s u m c > z − x sum_c > z-x sumc>z−x 立即剪枝
    • 当前 ∣ Δ ∣ |\Delta| ∣Δ∣ 与剩余位的和关系剪枝
  4. 二进制位数最多 31 位,DFS 深度有限

复杂度分析

  • 时间复杂度 : O ( Q ⋅ B ) O(Q \cdot B) O(Q⋅B),其中 B B B 为二进制位数(最大 31),DFS 最坏 O ( 3 B ) O(3^B) O(3B) 但剪枝高效
  • 空间复杂度 : O ( B ) O(B) O(B),存储二进制位信息

代码实现

cpp 复制代码
#include <bits/stdc++.h>
#define int long long
using namespace std;

int Q, x, y, z, k, bits[35], suffix[35];

int find(int L, int x) {
    if (L > x) return x + 1;
    int t = L;
    while (true) {
        if ((t & ~x) == 0) return t;
        int conflict = t & ~x;
        int low = conflict & -conflict;     
        t = (t + low) & ~(low - 1);         
        if (t > x) return x + 1;
    }
}

bool dfs(int pos, int sum_c, int delta) {
    if (pos == k + 1) {
        if (sum_c > z - x) return false;
        int d = abs(delta);
        return d <= y && d != 0;     
    }
    if (sum_c > z - x) return false;
    int rem = suffix[pos];            
    int d = abs(delta);
    if (d - rem > y) return false;
    if (d + rem <= y) 
        if (d > 0 || rem > 0) return true; 
    int val = bits[pos];
    if (dfs(pos + 1, sum_c + val, delta)) return true;
    if (dfs(pos + 1, sum_c, delta + val)) return true;
    if (dfs(pos + 1, sum_c, delta - val)) return true;
    return false;
}

signed main() {
    cin >> Q;
    while (Q--) {
        cin >> x >> y >> z;
        if (x > z) {
            cout << "NO\n";
            continue;
        }
        if (y >= x) {
            cout << "YES\n";
            continue;
        }
        int low = x & -x;
        if (y < low) {
            cout << "NO\n";
            continue;
        }
        vector<int> tmp;
        for (int i = 30; i >= 0; i--)
            if (x >> i & 1) tmp.push_back(1 << i);
        int dmin = 0;
        for (int v : tmp) dmin = abs(dmin - v);
        if (dmin <= y) {
            cout << "YES\n";
            continue;
        }
        int L = max(0ll, x - y), R = z - x;
        if (L <= R) {
            int c = find(L, x);
            if (c <= R) {
                if (c != x) {
                    cout << "YES\n";
                    continue;
                }
                c = find(c + 1, x);
                if (c <= R) {
                    cout << "YES\n";
                    continue;
                }
            }
        }
        k = 0;
        for (int i = 30; i >= 0; i--) {
            if (x >> i & 1) bits[++k] = (1 << i);
        }
        suffix[k + 1] = 0;
        for (int i = k; i >= 1; i--) 
            suffix[i] = suffix[i + 1] + bits[i];
        if (dfs(1, 0, 0)) cout << "YES\n";
        else cout << "NO\n";
    }
    return 0;
}

示例解释

样例输入

复制代码
2
5 2 9
3 9 2

输出

复制代码
YES
NO

解释

  • x = 5 , y = 2 , z = 9 x=5,y=2,z=9 x=5,y=2,z=9:取 a = 5 , b = 4 a=5,b=4 a=5,b=4 满足 5 ∣ 4 = 5 5\mid4=5 5∣4=5, ∣ 5 − 4 ∣ = 1 ≤ 2 |5-4|=1\leq2 ∣5−4∣=1≤2, 5 + 4 = 9 ≤ 9 5+4=9\leq9 5+4=9≤9
  • x = 3 , y = 9 , z = 2 x=3,y=9,z=2 x=3,y=9,z=2: a + b ≥ x = 3 > 2 a+b\geq x=3>2 a+b≥x=3>2,无解
相关推荐
Frostnova丶2 小时前
LeetCode 3296. 使山区高度为零的最少秒数
算法·leetcode
会员源码网2 小时前
抽象数据类型(ADT):理论与实践的桥梁
算法
像污秽一样2 小时前
算法设计与分析-习题4.5
数据结构·算法·排序算法·剪枝
Yupureki2 小时前
《C++实战项目-高并发内存池》4.CentralCache构造
c语言·开发语言·c++·单例模式·github
样例过了就是过了2 小时前
LeetCode热题100 全排列
数据结构·c++·算法·leetcode·dfs
2401_898075122 小时前
分布式系统监控工具
开发语言·c++·算法
程序员夏末2 小时前
【LeetCode | 第六篇】算法笔记
笔记·算法·leetcode
OKkankan3 小时前
撕 STL 系列:封装红黑树实现 mymap 和 myset
java·c++·算法
xh didida3 小时前
数据结构--实现链式结构二叉树
c语言·数据结构·算法