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