A | B
题目描述
给定两个正整数a,x,统计满足以下条件的b的个数:
1.1≤b≤x2. a∣b=a+b
输入描述
第一行给出一个 t, 1≤t≤105
接下来 t 行每行一对正整数a,x,1≤a,x≤109
输出描述
输出 t 行,每行一个正整数
Sample Input
2
1 2
2 3
Sample Output
1
1
思路
首先对题意进行理解。首先将数字a展开二进制,这样a中会有很多的0,那么你尽可能的用一个数去填补这个0,使之成为1,且这个数不会大于x。
很容易想到,当a二进制位数比x的二进制位数大的时候,大于x位的地方无论是填0或1都不满足条件,所以我门就可以缩小范围到x的二进制长度去探讨该问题。
cin >> num >> x;
while (num)
{
A[cnt1++] = num % 2;
num >>= 1;
}
while (x)
{
B[cnt2++] = x % 2;
x >>= 1;
}
len = cnt2;
reverse(A, A + len);
reverse(B, B + len);
以 275和 238举例子
100010011
11101110
首先对齐
1 0 0 0 1 0 0 1 1
0 1 1 1 0 1 1 1 0
将多余的部分删除
0 0 0 1 0 0 1 1
1 1 1 0 1 1 1 0
我们会发现在其中对应的情况有可能会有00,01,10,11
11的情况
很容易发现,当你当前选择的数比x小的时候,那么后面的数字无论如何选择都是成立的,如果想要合法,那么久要看你选择的后面有多少个0。
例如
1000
1010
你的第一位只能选择0,若选择1则构成的数一定不满足a|b=a+b
那么你构成的数的最大值也就是111也比1010小,所以可以放心构造。
那么就让这个三个位置可填可不填的种类,一共有23=8种,但是你不可以填写0,那么你就需要减去一种故有7种排法。
那么对于任何一组,都可以先忽略第一位的数字,去看剩下位置。
这样我们可以简化问题。
- 当第一位只能是0的时候,那么答案就是剩下空余位置的个数直接相关,ans=(1<<tot)-1
- 若第一位可以是1的时候,我们可以将第一位先不操作,去看剩下的,再去考虑第一位选择1的情况。
现在的核心问题是如何去处理剩下的。
现在就要对01这种情况,进行研究。
01这种情况更像是一个岔路口,即你可以选择0也可以选择1,显然当你选择0的时候,这种情况下可能会出现的情况就可以直接算得了,(ans=(1<<tot)-1)。当我们选择1的时候我们就要继续往下走,接着我们有可能又遇到选择的情况。整体感觉就像如图所示。
但是有一种情况是不可以忽略的,就是直走的路是无法填写的,只能是000这种可以满足,但这种情况是可以的,因为最高位是1,哪怕后面全是0也没有问题。
再接下来就是去判断“岔路口”的个数,以及后面有没有不可以处理的情况。
为了方便表示,我们用tot[002],tot[012],tot[102],tot[112],即tot[0]~tot[3]去记录不同的情况的个数,在没有岔路口的时候,我们可以通过这些个数的情况去判别是否走到了头,让答案需要加一(均为0)。
AC代码
#include<bits/stdc++.h>
#pragma GCC optimize(2)
using namespace std;
typedef long long ll;
int main()
{
int T;
cin >> T;
while (T--)
{
int A[64] = { 0 }, B[64] = { 0 }, len;
ll num, x, ans = 0, cnt1 = 0, cnt2 = 0;
int tot[4] = { 0 };
cin >> num >> x;
while (num)
{
A[cnt1++] = num % 2;
num >>= 1;
}
while (x)
{
B[cnt2++] = x % 2;
x >>= 1;
}
len = cnt2;
reverse(A, A + len);
reverse(B, B + len);
for (int i = 1; i < len; i++)
if (!A[i] && !B[i])tot[0]++;
else if (!A[i] && B[i])tot[1]++;
else if (A[i] && !B[i])tot[2]++;
else tot[3]++;
ans = (1ll << (tot[0] + tot[1])) - 1;
if (len == 1)
{
if (A[0] == 0 && B[0] == 1)
cout << 1 << endl;
else cout << 0 << endl;
}
else
{
if (!A[0])
for (int i = 1; i < len; i++)
{
if (tot[1] == 0 && !tot[3])
{
ans++;
break;
}
if (A[i] && B[i])
{
A[3]--;
ans += (1ll << (tot[0] + tot[1]));
break;
}
else if (!A[i] && B[i])
{
tot[1]--;
ans += (1ll << (tot[0] + tot[1]));
if (tot[1] == 0 && !tot[3])
{
ans++;
break;
}
}
else if (A[i] && !B[i])
tot[2]--;
else tot[0]--;
}
cout << ans << endl;
}
}
}
By-轮月