H Pair 数位DP入门

题意:在 [ 1 , A ] [ 1 , B ]分别选 x , y 使得 (x&y)>C || (x^y)<C  成立

思路:或命题 -> 正难则反

 (x&y) > c || (x^y)< C   否命题为 (x&y) <= c&& (x^y)>= C

与位运算有关,则化简为二进制的数位DP问题 

注意:DP 求出的解 x y 的取值为 [ 0 , A ] [ 0 , B ]  ,则应该多减去 x=0和y=0 的贡献 max(A-C+1,0)+max(B-C+1,0)

数位DP理解: 在一定区间内 dfs搜索合法解,利用高位数大小 确保 数在定义域区间内 并 从高位到地位枚举

#include<bits/stdc++.h>
#define ll long long 
using namespace std;
int A,B,C;
int a[35],b[35];//A B 二进制i位 的数
ll dp[35][2][2][2][2];
ll dfs(int pos,int sta1,int sta2,int lim1,int lim2)
{
	if(pos<0)
	{
		return 1;
	}
	
	if(dp[pos][sta1][sta2][lim1][lim2]!=-1) 
		return dp[pos][sta1][sta2][lim1][lim2];
	int up1 = lim1? a[pos]:1;
	int up2 = lim2? b[pos]:1;
	ll ans=0;
	for(int i=0;i<=up1;i++)
		for(int j=0;j<=up2;j++) 
		{
			int x=(C>>pos)&1;
			if(!sta1&&(i&j)>x) continue;//非法解 
			if(!sta2&&(i^j)<x) continue;
			int sta11=sta1|| ( (i&j)<x );// (i&j)==x时 sta11=0 ((i&j)>x) 已经continue 
			int sta22=sta2|| ( (i^j)>x );
			ans+=dfs(pos-1,sta11,sta22,lim1&&(i==up1),lim2&&(j==up2));
			//lim1 lim2 限制 x y 在[0,a]	[0,b] 定义域
			//sta1 sta2 限制 x&y<=c  x^y>=C 
		}
	
	return dp[pos][sta1][sta2][lim1][lim2]=ans; 
	//!!!记忆化!! 
	
}

int main()
{
	int T;
	cin>>T;
	while(T--)
	{
		cin>>A>>B>>C;
		ll ans=1ll*A*B;
		ans+=max(A-C+1,0)+max(B-C+1,0);//数为dp 多算了 x,y 出现0 的贡献 
		int AA=A,BB=B;
		memset(dp, -1, sizeof(dp));
		memset(a,0,sizeof(a));
		memset(b,0,sizeof(b));
		int ind=0;
		while(AA)
		{
			a[ind++]=AA%2;
			AA/=2;
		}		
		ind=0;
		while(BB)
		{
			b[ind++]=BB%2;
			BB/=2;
		}
		
		printf("%lld\n",ans-dfs(31,0,0,1,1));
		// (x&y) > c || (x^y)< C  的否命题为 (x&y) <= c && (x^y)>= C
		//正难则反 
		
	}
}

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值