第十八届同济大学程序设计竞赛H.三阳开泰(数位dp+二进制)

题目传送门

题意

给你 T ( 1 ≤ T ≤ 100 ) T(1\le T \le 100) T(1T100)组询问,每次询问四个数 A , B , C , X ( 1 ≤ A , B , C , X ≤ 1 0 18 A,B,C,X(1\le A,B,C,X\le 10^{18} A,B,C,X(1A,B,C,X1018,问你有多少个三元组 ( a , b , c ) (a,b,c) (a,b,c)满足 a a a xor b ≤ X b\le X bX, a a a xor c ≤ X c\le X cX, b b b xor c ≤ X c\le X cX

思路

这个数据范围很容易想到数位dp,要是加上下限,问你三个小区间内有多少就更像数位dp了。

我们用 f [ p o s ] [ l a ] [ l b ] [ l c ] [ l a b ] [ l a c ] [ l b c ] f[pos][la][lb][lc][lab][lac][lbc] f[pos][la][lb][lc][lab][lac][lbc]表示状态数组, p o s pos pos表示当前搜到了第 p o s pos pos位, l a , l b , l c la,lb,lc la,lb,lc分别表示当前搜索到的 a , b , c a,b,c a,b,c是否被 A , B , C A,B,C A,B,C限制, l a b , l a c , l b c lab,lac,lbc lab,lac,lbc则表示 a a a xor b b b, a a a xor c c c, b b b xor c c c是否被 X X X限制。然后套模板就行了。

C o d e Code Code

// Author : ACfunhsl
// Time : 2021/5/22 16:42:57
#define int long long
const int N = 1e6+50;
const int inf = 0x3f3f3f3f;
const int mod = 1e9+7;

int aa[N],bb[N],cc[N],xx[N],cnta,cntb,cntc,cntx;
int f[100][2][2][2][2][2][2];

int dfs(int pos,int la,int lb,int lc,int lab,int lac,int lbc)
{
	if(pos==-1) return 1;
	int &ans = f[pos][la][lb][lc][lab][lac][lbc];
	if(ans!=-1) return ans;
	ans = 0;
	int upa = la?aa[pos]:1;
	int upb = lb?bb[pos]:1;
	int upc = lc?cc[pos]:1;
	int upx = xx[pos];
	for(int i=0;i<=upa;i++)
	{
		for(int j=0;j<=upb;j++)
		{
			for(int k=0;k<=upc;k++)
			{
				if(lab && (i^j)>upx) continue;
				if(lac && (i^k)>upx) continue;
				if(lbc && (j^k)>upx) continue;
				ans = (ans + dfs(pos-1,la&&(i==upa),lb&&(j==upb),lc&&(k==upc),lab&&(i^j==upx),lac&&(i^k==upx),lbc&&(j^k==upx))%mod)%mod;
			}
		}
	}
	return ans;
}

int solve(int a,int b,int c,int x)
{
	cnta = cntb = cntc = cntx = 0;
	mem(aa,0);
	mem(bb,0);
	mem(cc,0);
	mem(xx,0);
	while(a)
		aa[cnta++] = a&1,a>>=1;
	while(b)
		bb[cntb++] = b&1,b>>=1;
	while(c)
		cc[cntc++] = c&1,c>>=1;
	while(x)
		xx[cntx++] = x&1,x>>=1;
	return dfs(63,1,1,1,1,1,1);
}

signed main()
{
	int t;
	cin>>t;
	while(t--)
	{
		int a,b,c,x;
		cin>>a>>b>>c>>x;
		memset(f,-1,sizeof f);
		cout<<solve(a,b,c,x)<<endl;
	}



	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值