女装OJ Contest #12 D XOR Pair 题解

题目描述

给出 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 ) (0xa,0yb)满足 x ⊕ y = n x \oplus y=n xy=n并且 ∣ x − y ∣ ≤ m |x-y| \le m xym,其中 ⊕ \oplus 是异或位运算。

输入输出格式

输入格式

输入有多组数据。第一行有一个整数 T T T ( 1 ≤ T ≤ 1 0 5 1 \le T \le 10^5 1T105),表示测试数据组数。然后对于每组数据:

第一行包含 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} 0a,b,n,m1018),含义如题所述。

输出格式

对于每组数据,输出一个非负整数表示这样的数对个数。

输入输出样例

输入样例 #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 xy会发生借位的情况)

由于题目是要求 x ⊕ y = n x\oplus y=n xy=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 xy的那一位可以是 − 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[nm,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 nm以及 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;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值