题目描述
给出 4 4 4个非负整数 a a a, b b b, n n n和 m m m,求出有多少数对 ( x , y ) (x,y) (x,y) ( 0 ≤ x ≤ a , 0 ≤ y ≤ b ) (0 \le x \le a, 0 \le y \le b ) (0≤x≤a,0≤y≤b)满足 x ⊕ y = n x \oplus y=n x⊕y=n并且 ∣ x − y ∣ ≤ m |x-y| \le m ∣x−y∣≤m,其中 ⊕ \oplus ⊕是异或位运算。
输入输出格式
输入格式
输入有多组数据。第一行有一个整数 T T T ( 1 ≤ T ≤ 1 0 5 1 \le T \le 10^5 1≤T≤105),表示测试数据组数。然后对于每组数据:
第一行包含 4 4 4个非负整数 a a a, b b b, n n n和 m m m( 0 ≤ a , b , n , m ≤ 1 0 18 0 \le a, b, n, m \le 10^{18} 0≤a,b,n,m≤1018),含义如题所述。
输出格式
对于每组数据,输出一个非负整数表示这样的数对个数。
输入输出样例
输入样例 #1
5
1 1 1 1
10 10 10 10
100 100 100 100
1000 1000 1000 1000
10000 10000 10000 10000
输出样例 #1
2
6
74
978
3618
说明
对于第 1 1 1个样例,合法的数对如下: ( 0 , 1 ) (0, 1) (0,1)和 ( 1 , 0 ) (1,0) (1,0)。
对于第 2 2 2个样例,合法的数对如下: ( 0 , 10 ) (0,10) (0,10), ( 2 , 8 ) (2, 8) (2,8), ( 3 , 9 ) (3,9) (3,9), ( 8 , 2 ) (8, 2) (8,2), ( 9 , 3 ) (9,3) (9,3)和 ( 10 , 0 ) (10,0) (10,0)。
本题直接做数位 d p dp dp其实是有点困难的(由于 x − y x-y x−y会发生借位的情况)
由于题目是要求 x ⊕ y = n x\oplus y=n x⊕y=n 我们可以把借位转换一下
假设做减法时不发生借位
讨论 n n n在二进制下每一位的情况,当 n n n某一位等于 0 0 0时, x x x和 y y y那一位要么都是 0 0 0要么都是 1 1 1或者都是 − 1 -1 −1,这样的话减完肯定都是 0 0 0。如果 n n n的某一位等于 1 1 1的话,那么 x − y x-y x−y的那一位可以是 − 1 -1 −1或者 1 1 1,这取决于 x x x的二进制表示的那一位是 0 0 0还是 1 1 1。
通过减法我们得到了一个全是 − 1 -1 −1, 0 0 0, 1 1 1的二进制串
假设每一位都是 − 1 -1 −1,那么就可以认为把第 i i i位变成 1 1 1的时候给加上了 2 i + 1 2^{i+1} 2i+1。
问题就变成了对于 − n -n −n进行一定的操作,可以选择一些 i i i,每次给它加上 2 i + 1 2^{i+1} 2i+1,使得加上之后的数不超过 m m m。
然后呢,其实我们选出来的 i i i是满足 i ∈ [ n − m , n + m ] i\in[n-m,n+m] i∈[n−m,n+m]
然后就可以进行数位 d p dp dp了
令 s o l v e ( d , e x , e y , e l , e r ) solve(d,ex,ey,el,er) solve(d,ex,ey,el,er)表示从高到低处理到了第 d d d位, x x x和 a a a的大小关系是 e x ex ex( 1 1 1表示相同, 0 0 0表示小于), y y y和 b b b的大小关系是 e y ey ey( 1 1 1表示相同, 0 0 0表示小于),最终选出来那个数和 n − m n-m n−m以及 n + m n+m n+m的大小关系分别是 e l el el和 e r er er。
转移就是枚举下 x x x和 y y y的第 i i i为分别是什么就行了
Code:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int twx=+100;
const int inf=0x7fffffff;
ll read()
{
ll sum=0;
ll flag=1;
char c=getchar();
while(c<'0'||c>'9')
{
if(c=='-')
{
flag=-1;
}
c=getchar();
}
while(c>='0'&&c<='9')
{
sum=((sum*10)+c-'0');
c=getchar();
}
return sum*flag;
}
ll dp[64][2][2][2][2];
//表示从高到低处理到了第i位,
//x和a的大小关系是ex(1表示相同,0表示小于),
//y和b的大小关系是ey(1表示相同,0表示小于),
//最终选出来那个数和n-m以及n+m的大小关系分别是el和er。
ll xs;
ll ys;
ll n;
ll m;
ll lm;
ll rm;
int t;
ll solve(int d,int ex,int ey,int el,int er)
{
if(d<0)
{
int olm=lm&1;
int orm=rm&1;
if(el&&er)
{
return olm<=0&&0<=orm;
}
if(el)
{
return olm<=0;
}
if(er)
{
return orm>=0;
}
return 1;
}
if(dp[d][ex][ey][el][er]!=-1)
{
return dp[d][ex][ey][el][er];
}
ll ret=0;
int ox=ex?xs>>d&1:1;
int oy=ey?ys>>d&1:1;
int on=n>>d&1;
int olm=el?lm>>(d+1)&1:0;
int orm=er?rm>>(d+1)&1:1;
for(int x=0;x<=ox;x++)
{
for(int y=0;y<=oy;y++)
{
if((x^y)!=on)
{
continue ;
}
if(x==y&&olm<=0&&0<=orm)
{
ret+=solve(d-1,ex&(x==ox),ey&(y==oy),el&(0==olm),er&(0==orm));
}
if(x==0&&y==1&&olm<=0&&0<=orm)
{
ret+=solve(d-1,ex&(x==ox),ey&(y==oy),el&(0==olm),er&(0==orm));
}
if(x==1&&y==0&&olm<=1&&1<=orm)
{
ret+=solve(d-1,ex&(x==ox),ey&(y==oy),el&(1==olm),er&(1==orm));
}
}
}
return dp[d][ex][ey][el][er]=ret;
}
void init()
{
t=read();
while(t--)
{
xs=read();
ys=read();
n=read();
m=read();
lm=max(1LL*0,n-m);
rm=n+m;
memset(dp,-1,sizeof dp);
printf("%lld\n",solve(62,1,1,1,1));
}
}
int main()
{
init();
return 0;
}