P15445 「IXOI R1」永远在一起! 题解
题目传送门 :P15445
题目大意
给定 Q Q Q 次询问,每次给出三个整数 x , y , z x, y, z x,y,z。需要判断是否存在两个不相等的自然数 a , b a, b a,b 满足:
- a ∣ b = x a \mid b = x a∣b=x(按位或运算)
- ∣ a − b ∣ ≤ y |a - b| \leq y ∣a−b∣≤y
- a + b ≤ z a + b \leq z a+b≤z
若存在输出 YES,否则输出 NO。
解题思路
核心观察
-
特判边界:
- 若 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)
-
二进制拆分特性:
- 将 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,可通过正负号分配构造解
-
子集构造法:
- 寻找 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)
-
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
算法流程
- 特判 x > z x>z x>z 直接返回
NO - 特判 y ≥ x y \geq x y≥x 直接返回
YES - 计算 l o w = x & − x low = x \& -x low=x&−x,若 y < l o w y < low y<low 返回
NO - 计算二进制位交替相减的 d m i n dmin dmin,若 d m i n ≤ y dmin \leq y dmin≤y 返回
YES - 在区间 [ 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 的值
- 若步骤 5 失败,使用 DFS 进行二进制位分配搜索
- 根据搜索结果输出
YES或NO
正确性证明
- 边界特判覆盖了无解和易解情况
- 子集构造法保证 a ∣ b = x a \mid b=x a∣b=x 且满足差值约束
- DFS 分配模拟所有可能的位分配方案,剪枝策略保证效率:
- 当前 s u m c > z − x sum_c > z-x sumc>z−x 立即剪枝
- 当前 ∣ Δ ∣ |\Delta| ∣Δ∣ 与剩余位的和关系剪枝
- 二进制位数最多 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,无解