2019牛客多校第七场——H.Pair【数位DP】

链接:https://ac.nowcoder.com/acm/contest/887/H

题目描述

Given three integers , , . Count the number of pairs <x ,yx\ , yx ,y> (with 1≤x≤A1 \leq x \leq A1≤x≤A and 1≤y≤B1 \leq y \leq B1≤y≤B)
such that at least one of the following is true:
- (x and yx\ and\ yx and y) >
- (x xor yx\ xor\ yx xor y) <

("and", "xor" are bit operators)

输入描述:

The first line of the input gives the number of test cases, T (T≤100)T\ (T \leq 100)T (T≤100).  test cases follow.

For each test case, the only line contains three integers ,  and .
1≤A,B,C≤1091 \leq A,B,C \leq 10^91≤A,B,C≤109

输出描述:

For each test case, the only line contains an integer that is the number of pairs satisfying the condition given in the problem statement.

输入

3
3 4 2
4 5 2
7 8 5

输出

5
7
31

题意:

给出A B C三个数,令x∈[1,A],y∈[1,B],找到满足x&y>=C且x^y<=C的<x,y>的对数

分析:

很显然是一个二进制的数位dp,但是我场上没想出来

dp[i][a][b][c][d] 表示 x前i位是否和A相同 y前i位是否和B相同 x&y前i位是否和C相同 x^y前i位是否和C相同 时的不成立的方案数,i从大到小转移选择当前的x和y这一位的取值

#include<bits/stdc++.h>
#define ll long long
using namespace std;
ll dp[32][2][2][2][2];
// dp[i][a][b][c][d] 
// x前i位是否和A相同 y前i位是否和B相同
// x&y前i位是否和C相同 x^y前i位是否和C相同
// 时的不成立的方案数
// i从大到小转移选择当前的x和y
int A,B,C;
void update(int i,int a,int b,int c,int d)
{
	//得到前i+1位此状态下可以转移到的前i位的所有状态 并更新答案
	//也就是说通过在合理范围内改变第i位的取值扩大方案数
	for (int j=0;j<=1;j++)
		for (int k=0;k<=1;k++)
		{
			int aa=0,bb=0,cc=0,dd=0;
			if(a)
			{
				if(j && !((A>>i)&1)) continue;//10 当前枚举大于A 不成立
				if(!j && ((A>>i)&1)) aa=0;//01 不相同
				else aa=1;//00或11 相同
			} 
			if(b)
			{
				if(k && !((B>>i)&1)) continue;
				if(!k && ((B>>i)&1)) bb=0;
				else bb=1;
			}
			if(c)
			{
				if((j&k) && !((C>>i)&1)) continue;
				if(!(j&k) && ((C>>i)&1)) cc=0;
				else cc=1;
			}
			if(d)
			{
				if(!(j^k) && ((C>>i)&1)) continue;
				if((j^k) && !((C>>i)&1)) dd=0;
				else dd=1;
			}
			dp[i][aa][bb][cc][dd]+=dp[i+1][a][b][c][d];
		}
}
int main()
{
	int t;scanf("%d",&t);
	while (t--)
	{
		scanf("%d%d%d",&A,&B,&C);
		memset(dp,0,sizeof(dp));
		dp[31][1][1][1][1]=1;//此时不成立
		ll ans=1ll*A*B+max(A-C+1,0)+max(B-C+1,0);
		for(int i=30;i>=0;i--)
			for (int a=0;a<=1;a++)
				for (int b=0;b<=1;b++)
					for (int c=0;c<=1;c++)
						for (int d=0;d<=1;d++)
							if(dp[i+1][a][b][c][d]) update(i,a,b,c,d);//此状态下前i+1位可行 向i转移
		for (int a=0;a<=1;a++)
				for (int b=0;b<=1;b++)
					for (int c=0;c<=1;c++)
						for (int d=0;d<=1;d++)
							ans-=dp[0][a][b][c][d];
		printf("%lld\n",ans);
	}
	return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值