H.Pair
題意
給出三個數
求出 對數使得或者
思路
-
首先易知這是求二進制問題
-
可以將問題轉化爲 * *總個數減去並且的個數 * *
-並且符合或者 -
在一個區間內符合,看起來像** 數位DP**
-由於處理二進制,我們可以 * 同時統計兩個數的01進制*
-在數位DP時維護兩個
狀態
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';
}
}