题目链接:https://ac.nowcoder.com/acm/contest/887/H
题意:给你 三个数A,B,C让你从 1~A内选择一个X,1~B内选择一个Y
找出<X,Y>的对数,使得 X&Y > C || X^Y < C
平常做的数位dp都是从一个区间内找数的个数,而现在是从两个区间找数对的对数,那就在之前的三维dp上多开两维,之前是枚举一个区间上限,现在改成两个区间上限,其他的和普通的数位dp一样
注意数位dp找到的是从0开始的 0 & Y 和 X&0 一定都小于C 不需要管
但是 0 ^ Y 和 X^0 当Y 和 X 小于C时 是满足的 所以结果还需要减去 ^运算 X 或 Y 为零时的情况
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <map>
#include <vector>
#include <queue>
using namespace std;
#define ll long long
#define int long long
const int maxn=1e5+5;
ll dp[32][3][3][2][2];
int a[32], b[32],c[32];
ll dfs1(int pos, int And, int Xor, int limit1, int limit2){
if(pos == -1) {
if(And == 1 || Xor == 2) return 1;
return 0;
}
if(dp[pos][And][Xor][limit1][limit2] != -1) return dp[pos][And][Xor][limit1][limit2];
int up1 = limit1 ? a[pos] : 1;
int up2 = limit2 ? b[pos] : 1;
ll ans = 0;
for(int i = 0; i <= up1; i++) {
for(int j = 0; j <= up2; j++) {
int x = i & j, y = i ^ j;
int _and = And;
int _xor = Xor;
if(And == 0) {
if(x > c[pos]) _and = 1;
else if(x < c[pos]) _and = 2;
}
if(Xor == 0) {
if(y > c[pos]) _xor = 1;
else if(y < c[pos]) _xor = 2;
}
ans += dfs1(pos - 1, _and, _xor, (i == up1) && limit1, limit2 &&(j == up2));
}
}
dp[pos][And][Xor][limit1][limit2] = ans;
return ans;
}
signed main()
{
int T, A, B, C;
scanf("%lld", &T);
while(T--) {
memset(dp, -1, sizeof(dp));
scanf("%lld%lld%lld", &A, &B, &C);
int i = 0, j = 0, k = 0;
int ans = min(C - 1, A) + min (C - 1, B) + 1;
while(i < 30) {
a[i++] = (A & 1); A>>=1;
}
while(j < 30) {
b[j++] = (B & 1); B>>=1;
}
while(k < 30) {
c[k++] = (C & 1); C>>=1;
}
printf("%lld\n", dfs1(29,0,0,1,1) - ans);
}
return 0;
}