链接: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;
}