2019牛客多校第七场H Pair 数位DP
题意:给你一个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)); } }