注 意 到 x 和 s 最 大 才 40 位 二 进 制 注意到x和s最大才40位二进制 注意到x和s最大才40位二进制
想 到 了 什 么 ? 爆 搜 ! ! \color{Red}想到了什么?爆搜!! 想到了什么?爆搜!!
从 第 40 位 二 进 制 开 始 爆 搜 从第40位二进制开始爆搜 从第40位二进制开始爆搜
当 x 的 这 一 位 是 1 , 说 明 两 个 数 字 二 进 制 在 这 位 有 且 仅 有 一 个 是 1 \color{orange}当x的这一位是1,说明两个数字二进制在这位有且仅有一个是1 当x的这一位是1,说明两个数字二进制在这位有且仅有一个是1
所 以 这 里 方 案 数 乘 上 2 , 和 加 上 这 一 位 二 进 制 继 续 往 下 搜 所以这里方案数乘上2,和加上这一位二进制继续往下搜 所以这里方案数乘上2,和加上这一位二进制继续往下搜
当 x 这 一 位 是 0 , 说 明 两 个 数 字 二 进 制 在 这 位 要 么 都 是 0 , 要 么 都 是 1 \color{Red}当x这一位是0,说明两个数字二进制在这位要么都是0,要么都是1 当x这一位是0,说明两个数字二进制在这位要么都是0,要么都是1
是 0 就 什 么 都 不 加 , 继 续 往 下 搜 是0就什么都不加,继续往下搜 是0就什么都不加,继续往下搜
是 1 就 加 上 这 一 位 二 进 制 的 两 倍 ( 两 个 数 ) , 继 续 往 下 搜 是1就加上这一位二进制的两倍(两个数),继续往下搜 是1就加上这一位二进制的两倍(两个数),继续往下搜
剪枝
很好想,当两个数后面的二进制全都选1的和还是小于s,结束
当两个数和已经大于s,结束
细节
我们这样搜可能搜出某个数为0的情况
但题目要求是正数
所以搜的时候记录一下,有没有选过1,没得话就减掉
#include
using namespace std;
typedef long long ll;
ll fac[100009],s,x,pre[10009],ans;
void dfs(ll num,ll u,ll k,ll ok)//num是哪一位二进制,u表示当前总和
{
if( num==-1 )
{
if( u==s )
{
ans+=fac[k];
if( !ok ) ans-=2;
}
return;
}
if( x&fac[num] )
dfs(num-1,u+fac[num],k+1,ok);//一个选0,一个选1
else
{
int flag=0;
if( u+2*fac[num]<=s ) flag=1,dfs(num-1,u+2*fac[num],k,1);//选1
if( pre[num-1]*2+u>=s ) flag=1,dfs(num-1,u,k,ok);
}
}
int main()
{
fac[0]=1,pre[0]=1;
for(int i=1;i<=40;i++) fac[i]=2*fac[i-1],pre[i]=pre[i-1]+fac[i];
cin >> s >> x;
dfs(40,0,0,0);
cout << ans;
}