题意:给你一个3个数A, B, C问有多少对pair(i, j),1 <= i <= A, 1 <= j <= B, i AND j > C或 i XOR j < C。A, B, C范围为1e9.
思路:场上一看以为是推式子加什么筛做,无果。之后才知道是数位DP(以下思路来自学长的代码orz)。首先,我们可以把问题转化为求i AND j < C并且 i XOR j > C的数对个数,用总数(A* B)减去这个数。我们在DP过程中设置几个变量:ok1, 之前已经填的i 和 j的位是否已经满足i AND j < C, ok2同理。lim1表示i是否可以随便填,lim2同理。zero1表示上面填的位是否全是0,zero2同理。dp的时候,记忆化搜索就行。DP过程很直观就不解释了。(写了那么多数位DP赛场上想不到,太菜了QAQ)
代码:
#include <bits/stdc++.h>
#define LL long long
using namespace std;
const int maxn = 55;
int a[maxn], b[maxn], c[maxn];
LL dp[maxn][2][2][2][2][2][2];
LL dfs(int dep, bool ok1, bool ok2, bool lim1, bool lim2, bool zero1, bool zero2) {
if(dep == -1) {
return (zero1 == 0 && zero2 == 0);
}
if(dp[dep][ok1][ok2][lim1][lim2][zero1][zero2] != -1) {
return dp[dep][ok1][ok2][lim1][lim2][zero1][zero2];
}
int mx1, mx2;
if(lim1) mx1 = a[dep];
else mx1 = 1;
if(lim2) mx2 = b[dep];
else mx2 = 1;
LL ans = 0;
for (int i = 0; i <= mx1; i++) {
for (int j = 0; j <= mx2; j++) {
if(!ok1 && ((i & j) > c[dep])) continue;
if(!ok2 && ((i ^ j) < c[dep])) continue;
ans += dfs(dep - 1, ok1 || ((i & j) < c[dep]),
ok2 || ((i ^ j) > c[dep]), lim1 && (i == mx1),
(lim2 && j == mx2), zero1 && (i == 0), zero2 && (j == 0));
}
}
dp[dep][ok1][ok2][lim1][lim2][zero1][zero2] = ans;
return ans;
}
LL solve(LL A, LL B, LL C) {
LL res = A * B;
memset(a, 0, sizeof(a));
memset(b, 0, sizeof(b));
memset(c, 0, sizeof(c));
memset(dp, -1, sizeof(dp));
for (int i = 0; i <= 35; i++) {
a[i] = A & 1; A >>= 1;
b[i] = B & 1; B >>= 1;
c[i] = C & 1; C >>= 1;
}
return res - dfs(35, 0, 0, 1, 1, 1, 1);
}
int main() {
int T;
LL A, B, C;
scanf("%d", &T);
while(T--) {
scanf("%lld%lld%lld", &A, &B, &C);
printf("%lld\n", solve(A, B, C));
}
}