H.Pair
题意
给出三个数 A , B , C A,B, C A,B,C
求出 p a i r < i , j > pair<i, j > pair<i,j> 对数使得 i ∧ j > C i\land j > C i∧j>C或者 i ⊕ j < C i \oplus j<C i⊕j<C
1 ≤ i ≤ A , 1 ≤ j ≤ B 1 \leq i \leq A,1\leq j \leq B 1≤i≤A,1≤j≤B
思路
-
首先易知这是求二进制问题
-
可以将问题转化为 * *总个数减去 i ∧ j ≤ C i\land j\leq C i∧j≤C并且 i ⊕ j ≥ C i \oplus j \ge C i⊕j≥C的个数 * *
- 1 ≤ i ≤ A , 1 ≤ j ≤ B 1 \leq i \leq A,1\leq j \leq B 1≤i≤A,1≤j≤B并且符合 i ∧ j > C i\land j>C i∧j>C或者 i ⊕ j < C i \oplus j < C i⊕j<C -
在一个区间内符合 f ( i ) f(i) f(i),看起来像** 数位DP**
-由于处理二进制,我们可以 * 同时统计两个数的01进制*
-在数位DP时维护两个 l i m i t , l e a d limit,lead limit,lead
状态
d p [ p o s ] [ s t 1 ] [ s t 2 ] [ l e a d 1 ] [ l e a d 2 ] [ l i m i t 1 ] [ l i m i t 2 ] dp[pos][st1][st2][lead1][lead2][limit1][limit2] dp[pos][st1][st2][lead1][lead2][limit1][limit2]
pos为当前位置,st1维护条件1成立,st2维护条件2成立
直接将所有状态记录下来,反之复杂度也不会爆
** 由于不存在等于0的情况,所以记录前导0的状态是必须的**
当然,先处理出包含0的对数,再减去和0达成条件的对数也可以(前导0就不必要)
代码
预处理
LL part(int x, int y, int z) {
memset(a, 0, sizeof(a));
memset(b, 0, sizeof(b));
memset(c, 0, sizeof(c));
memset(dp, -1, sizeof(dp));
LL res = LL(x) * y; //转化问题为总个数减去并且的情况
int l1, l2, l3; l1 = l2 = l3 = 0; //拆分为二进制数
while (x)a[++l1] = x & 1, x >>= 1;
while (y)b[++l2] = y & 1, y >>= 1;
while (z)c[++l3] = z & 1, z >>= 1;
len = max(l1, max(l2, l3));
return res - dfs(1, 0, 0, 1, 1, 1, 1);
}
数位DP
LL dp[maxn][2][2][2][2][2][2];
LL dfs(int pos, bool st1, bool st2, bool lead1, bool lead2, bool limit1, bool limit2)
//pos为位置,st1为条件1成立,st2为条件2成立,lead为前导0,limit为最高位
{
if (pos > len) return (!lead1) && (!lead2); //A>=1&&B>=1不存在i==0||j==0的对
if (dp[pos][st1][st2][lead1][lead2][limit1][limit2] != -1)
return dp[pos][st1][st2][lead1][lead2][limit1][limit2]; //记忆化
if (st1 && st2 && !limit1 && !limit2 && !lead1 && !lead2) //由于01所以这个处理反而慢了qwq
return (LL(1) << (len - pos + 1)) * (1 << (len - pos + 1));
LL res = 0;
int top1 = limit1 ? a[len - pos + 1] : 1;
int top2 = limit2 ? b[len - pos + 1] : 1;
for (int i = 0; i <= top1; i++) {
for (int j = 0; j <= top2; j++) {
if (!st1 && (i & j) > c[len - pos + 1]) continue;//i&j>C不符合条件
if (!st2 && (i ^ j) < c[len - pos + 1]) continue;//i^j<C不符合条件
res += dfs(pos + 1, st1 || (i & j) < c[len - pos + 1], st2 || (i ^ j) > c[len - pos + 1],
(!i) && lead1, (!j) && lead2, limit1 && i == top1, limit2 && j == top2);
}
}
dp[pos][st1][st2][lead1][lead2][limit1][limit2] = res;//记忆化
return res;
}
AC
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
using namespace std;
typedef long long LL;
const int maxn = 35;
int len, a[maxn], b[maxn], c[maxn];
//x&y<=C && x|y>=c
LL dp[maxn][2][2][2][2][2][2];
LL dfs(int pos, bool st1, bool st2, bool lead1, bool lead2, bool limit1, bool limit2) {
if (pos > len) return (!lead1) && (!lead2);
if (dp[pos][st1][st2][lead1][lead2][limit1][limit2] != -1)
return dp[pos][st1][st2][lead1][lead2][limit1][limit2];
if (st1 && st2 && !limit1 && !limit2 && !lead1 && !lead2)
return (LL(1) << (len - pos + 1)) * (1 << (len - pos + 1));
LL res = 0;
int top1 = limit1 ? a[len - pos + 1] : 1;
int top2 = limit2 ? b[len - pos + 1] : 1;
for (int i = 0; i <= top1; i++) {
for (int j = 0; j <= top2; j++) {
if (!st1 && (i & j) > c[len - pos + 1]) continue;
if (!st2 && (i ^ j) < c[len - pos + 1]) continue;
res += dfs(pos + 1, st1 || (i & j) < c[len - pos + 1], st2 || (i ^ j) > c[len - pos + 1],
(!i) && lead1, (!j) && lead2, limit1 && i == top1, limit2 && j == top2);
}
}
dp[pos][st1][st2][lead1][lead2][limit1][limit2] = res;
return res;
}
LL part(int x, int y, int z) {
memset(a, 0, sizeof(a));
memset(b, 0, sizeof(b));
memset(c, 0, sizeof(c));
memset(dp, -1, sizeof(dp));
LL res = LL(x) * y;
int l1, l2, l3; l1 = l2 = l3 = 0;
while (x)a[++l1] = x & 1, x >>= 1;
while (y)b[++l2] = y & 1, y >>= 1;
while (z)c[++l3] = z & 1, z >>= 1;
len = max(l1, max(l2, l3));
return res - dfs(1, 0, 0, 1, 1, 1, 1);
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
int t, a, b, c; cin >> t;
while (t--) {
cin >> a >> b >> c;
cout << part(a, b, c) << '\n';
}
}