P10118 『STA - R4』And
题目分析
这道题感觉不错。
先从 \(x \operatorname{AND} y = B\) 下手。
若 \(B\) 中第 \(x\) 位的数为 \(1\),那么 \(x\) 和 \(y\) 对应的这一位必须为 \(1\);若 \(B\) 中第 \(x\) 位的数为 \(0\),那么 \(x\) 和 \(y\) 对应的这一位不能有一个为 \(1\)(废话)。
所以我们可以写钦定 \(x=y=B\),这样才满足 \(x \operatorname{AND} y = B\),所以 \(A\le 2B\) 可以特判 \(0\) 了。
那么这样 \(A\) 仍有 \(A-2B\) 要分配,分配在 \(x,y\) 的某些位置上,很显然一个位置不能分配两个 \(1\) 在 \(x,y\) 上。令 \(k=A-2B\),那么它二进制下的每个 \(1\) 就意味着要分到 \(x,y\) 上。当然若 \(k \operatorname{AND} B \ne 0\) 的话也是无解。
\(k\) 的 \(1\) 要怎么分配?因为有 \(x\le y\),所以最高位置上的 \(1\) 应该给到 \(y\),然后后面的几位就可以随意的分到 \(x,y\) 了,然后答案就是这些所有情况的差值之和了,然后怎么求呢?
举个例子,对于 \(k=(10110)_2\),\(x=y=B=(1001)_2\),所有答案为:
\(x=(1001)_2+(00\textcolor{red}{11}0)_2,y=(1001)_2+(10000)_2\)
\(x=(1001)_2+(00\textcolor{red}100)_2,y=(1001)_2+(100\textcolor{red}{1}0)_2\)
\(x=(1001)_2+(000\textcolor{red}10)_2,y=(1001)_2+(10\textcolor{red}{1}00)_2\)
\(x=(1001)_2+(00000)_2,y=(1001)_2+(10\textcolor{red}{11}0)_2\)
记 \(pos\) 为 \(k\) 的最高位的下标,记 \(s\) 为 \(k\) 去除最高位后的数,\(c\) 为 \(s\) 在二进制下的 \(1\) 的个数。所有红色部分构成了 \(s\) 的所有子集,这些子集之和为 \(2^{c-1}s\),那么答案可以理解为 \(k\) 减去一个子集后再减去一个子集。答案为 \(2^ck-2\times2^{c-1}s=2^ck-2^cs=2^c\times 2^{pos}\)。
代码
#include <bits/stdc++.h>
#define int long long
#define fi first
#define se second
#define pb push_back
#define mk make_pair
#define ll long long
#define space putchar(' ')
#define enter putchar('\n')
using namespace std;
inline int read() {
int x = 0, f = 1;
char c = getchar();
while (c < '0' || c > '9') f = c == '-' ? -1 : f, c = getchar();
while (c >= '0' && c <= '9') x = (x<<3)+(x<<1)+(c^48), c = getchar();
return x*f;
}
inline void write(int x) {
if (x < 0) x = -x, putchar('-');
if (x > 9) write(x/10);
putchar('0'+x%10);
}
int t, m;
int solve() {
int a = read(), b = read(), x = a-b*2;
if (b*2 >= a || x&b) return 0;
int pos = 63-__builtin_clzll(x), c = __builtin_popcountll(x^(1ll<<pos));
return (1ll<<c)%m*((1ll<<pos)%m)%m;
}
signed main() {
t = read(), m = read();
while (t--) write(solve()), enter;
return 0;
}