2019牛客多校第七场H Pair 数位DP

题意:给你一个3个数A, B, C问有多少对pair(i, j),1 <= i <= A, 1 <= j <= B, i AND j > C或 i XOR j < C。A, B, C范围为1e9.

思路:场上一看以为是推式子加什么筛做,无果。之后才知道是数位DP(以下思路来自学长的代码orz)。首先,我们可以把问题转化为求i AND j < C并且 i XOR j > C的数对个数,用总数(A* B)减去这个数。我们在DP过程中设置几个变量:ok1, 之前已经填的i 和 j的位是否已经满足i AND j < C, ok2同理。lim1表示i是否可以随便填,lim2同理。zero1表示上面填的位是否全是0,zero2同理。dp的时候,记忆化搜索就行。DP过程很直观就不解释了。(写了那么多数位DP赛场上想不到,太菜了QAQ)

代码:

#include <bits/stdc++.h>
#define LL long long
using namespace std;
const int maxn = 55;
int a[maxn], b[maxn], c[maxn];
LL dp[maxn][2][2][2][2][2][2];
LL dfs(int dep, bool ok1, bool ok2, bool lim1, bool lim2, bool zero1, bool zero2) {
	if(dep == -1) {
		return (zero1 == 0 && zero2 == 0);
	}
	if(dp[dep][ok1][ok2][lim1][lim2][zero1][zero2] != -1) {
		return dp[dep][ok1][ok2][lim1][lim2][zero1][zero2];
	}
	int mx1, mx2;
	if(lim1) mx1 = a[dep];
	else mx1 = 1;
	if(lim2) mx2 = b[dep];
	else mx2 = 1;
	LL ans = 0;
	for (int i = 0; i <= mx1; i++) {
		for (int j = 0; j <= mx2; j++) {
			if(!ok1 && ((i & j) > c[dep])) continue;
			if(!ok2 && ((i ^ j) < c[dep])) continue;
			ans += dfs(dep - 1, ok1 || ((i & j) < c[dep]), 
			ok2 || ((i ^ j) > c[dep]), lim1 && (i == mx1), 
			(lim2 && j == mx2), zero1 && (i == 0), zero2 && (j == 0));
		}
	}
	dp[dep][ok1][ok2][lim1][lim2][zero1][zero2] = ans;
	return ans;
}
LL solve(LL A, LL B, LL C) {
	LL res = A * B; 
	memset(a, 0, sizeof(a));
	memset(b, 0, sizeof(b));
	memset(c, 0, sizeof(c));
	memset(dp, -1, sizeof(dp));
	for (int i = 0; i <= 35; i++) {
		a[i] = A & 1; A >>= 1;
		b[i] = B & 1; B >>= 1;
		c[i] = C & 1; C >>= 1;
	}
	return res - dfs(35, 0, 0, 1, 1, 1, 1);
}
int main() {
	int T;
	LL A, B, C;
	scanf("%d", &T);
	while(T--) {
		scanf("%lld%lld%lld", &A, &B, &C);
		printf("%lld\n", solve(A, B, C));
	}
}

  

posted @ 2019-08-08 23:47  维和战艇机  阅读(476)  评论(0编辑  收藏  举报