牛客網暑期ACM多校訓練營(第七場)H.Pair 數位DP

H.Pair


題意

給出三個數 A,B,CA,B, C

求出 pair<i,j>pair<i, j > 對數使得ij>Ci\land j > C或者ij<Ci \oplus j<C

1iA1jB1 \leq i \leq A,1\leq j \leq B

思路

  • 首先易知這是求二進制問題

  • 可以將問題轉化爲 * *總個數減去ijCi\land j\leq C並且ijCi \oplus j \ge C的個數 * *
    -1iA1jB1 \leq i \leq A,1\leq j \leq B並且符合ij>Ci\land j>C或者ij<Ci \oplus j < C

  • 在一個區間內符合f(i)f(i),看起來像** 數位DP**
    -由於處理二進制,我們可以 * 同時統計兩個數的01進制*
    -在數位DP時維護兩個limitleadlimit,lead

狀態

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';
	}
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章