给出A, B, C, 在1~A中找一个数x, 1~B中找一个数y, 使得x&y>C或x^y<C, 问这样的二元组(x,y)有多少对.
原来数位dp可以这么骚的...虽然大佬们说这是个裸题...
因为^和&运算都是位运算, 所以把ABC都分为2进制表示.
我们考虑最暴力的做法: 枚举x, 枚举y 判断是不是满足题意. 在这个过程中, 我们发现做了很多无用功, 例如, 如果x和y的高位&答案已经比C的高位要大, x和y的低位不论取什么值不会影响结果, 反之亦然. 所以我们可以从高位向下数位dp, 每个位置保存x和y高位的大小信息. 这个题有趣在于数位dp不仅是关于一个数, 而是关于ABC三个数的. 其实只是在不断的在枚举A里的x和B里的y, C只是用来判断答案. 两个数当然会有两个lim限制, 对于x和y不能取0, 所以要特别记录x和y任意一个不能每一位全为0.
What's New: 我的写法是7维, 2维记录lim, 2维记录高位大小信息, 2维记录全零情况.
实际上, 可以5维, 舍弃记录全零的2维, 这样算出来的是0~A选择x, 0~B选择y的答案, 利用容斥的思想, 把a设为0, 答案减掉, 把a恢复, b设为0, 再算一遍减掉, 再把0 0 的情况加上. 这样, 我们无意中发现, 这样的写法可以解决这样的题:
给出A1, A2, B1, B2, C, 在A1~A2中找一个数x, B1~B2中找一个数y, 使得x&y>C或x^y<C, 问这样的二元组(x,y)有多少对.
通过这个题, 我发现, 数位dp不仅可以解决一个数有限制的问题, 还可以解决两个数有限制的问题.
#include<bits/stdc++.h>
#define enter puts("")
using namespace std;
typedef long long ll;
inline ll read() {
ll x = 0;
int f = 0;
char ch = getchar();
while (ch < '0' || ch > '9') f |= (ch == '-'), ch = getchar();
while (ch >= '0' && ch <= '9') x = (x << 3) + (x << 1) + ch - '0', ch = getchar();
return x = f ? -x : x;
}
inline void write(ll x) {
if (x == 0) {
putchar('0');
return;
}
if (x < 0) {
putchar('-');
x = -x;
}
static char s[23];
int l = 0;
while (x != 0)s[l++] = x % 10 + 48, x /= 10;
while (l)putchar(s[--l]);
}
int _;
ll A, B, C;
int a[40], b[40], c[40];
ll dp[40][2][2][3][3][2][2];
// pos limA limB
// and a and b compare c: <:0 ==:1 >:2
// xor a and b compare c: <:0 ==:1 >:2
// 0: notZero 1:isAllZero
ll dfs(int pos, int limA, int limB, int andCmp, int xorCmp, int aIsZero, int bIsZero) {
if (pos <= 0)
return (andCmp == 2 || xorCmp == 0) && aIsZero == 0 && bIsZero == 0;
ll &ans = dp[pos][limA][limB][andCmp][xorCmp][aIsZero][bIsZero];
if (ans != -1)return ans;
ans = 0;
int mA = limA ? a[pos] : 1, mB = limB ? b[pos] : 1;
for (int i = 0; i <= mA; ++i) {
for (int j = 0; j <= mB; ++j) {
int nxtAndCmp, nxtXorCmp;
if (andCmp == 0)nxtAndCmp = 0;
else if (andCmp == 2)nxtAndCmp = 2;
else {
if ((i & j) == c[pos])nxtAndCmp = 1;
else if ((i & j) < c[pos])nxtAndCmp = 0;
else nxtAndCmp = 2;
}
if (xorCmp == 0)nxtXorCmp = 0;
else if (xorCmp == 2)nxtXorCmp = 2;
else {
if ((i ^ j) == c[pos])nxtXorCmp = 1;
else if ((i ^ j) < c[pos])nxtXorCmp = 0;
else nxtXorCmp = 2;
}
ans += dfs(pos - 1, limA && i == mA, limB && j == mB,
nxtAndCmp, nxtXorCmp, aIsZero && i == 0, bIsZero && j == 0);
}
}
return ans;
}
void init() {
_ = read();
while (_--) {
memset(dp, -1, sizeof(dp));
A = read(), B = read(), C = read();
int pos = 0;
while (pos < 33) {
a[++pos] = A & 1, b[pos] = B & 1, c[pos] = C & 1;
A >>= 1, B >>= 1, C >>= 1;
}
write(dfs(32, 1, 1, 1, 1, 1, 1));
enter;
}
}
int main() {
#ifndef ONLINE_JUDGE
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
#endif
init();
return 0;
}