Comet OJ - Contest #12 :D.XOR Pair (数位dp)

在这里插入图片描述


如果没有条件 ∣ x − y ∣ ≤ m |x-y|\leq m xym,那么这题可以直接按 二进制位 枚举 x , y x,y x,y,满足 0 ≤ x ≤ a , 0 ≤ y ≤ b 0 \leq x \leq a,0 \leq y \leq b 0xa0yb,且 x ⨁ y = n x \bigoplus y =n xy=n

对于条件 : ∣ x − y ∣ ≤ m |x-y|\leq m xym 可以拆分为: m + y − x ≥ 0 m + y - x \geq 0 m+yx0 m + x − y ≥ 0 m + x - y \geq 0 m+xy0

由于有加减法,会产生进位和借位,对进位和借位模拟会非常麻烦,在网上题解参考的做法是,不模拟进位和借位,用 [-1,2] 四种值来表示这一位的值,当取值为 − 1 -1 1 时,就表示借位,取值为 2 2 2 时,就表示进位。

这么做的好处是我们不需要去纠结进位和借位的问题了,他们都有 “4进制” 的表示(因为这里取4种值,姑且称为4进制吧),但怎样的表示是合法的,即最后的值是 ≥ 0 \geq 0 0 的?

如果高位有一位大于等于 1,那么后面的位即使全部取 -1,最后的值也是不小于0的。
如果高位有一位小于 -1,那么后面的位即使全部取 2,最后的值也是小于0的。

高位如果有 -1的话,后面的位可以进位上来消掉。dp 应满足无后效性,在枚举每一位的取值的时候,我们可以把这一位的影响推到下一位,例如第 i i i m + y − x = v m + y - x = v m+yx=v,它对第 i − 1 i - 1 i1 位的影响为 2 ∗ v 2*v 2v,这样第 i − 1 i - 1 i1 位的取值就和前面的高位无关了,从而消除后效性。

按照这种思想,当枚举到第 i i i 位时,可以认为前面的高位都 “满足了限制”,只要在最后一位,也就是最低位时,满足这一位的值是不小于0的,那么它的真实值一定也是不小于0的,我们就找到了一个合法解。


代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int t;
ll a, b, n, m;
ll dp[64][3][3];
ll dfs(int pos,int v1,int v2,int lx,int ly) {
    
    
	v1 = min(v1,1); v2 = min(v2,1);
	if (v1 < -1 || v2 < -1) return 0;
	if (pos < 0) return v1 >= 0 && v2 >= 0;
	if (!lx && !ly && dp[pos][v1 + 1][v2 + 1] != -1) return dp[pos][v1 + 1][v2 + 1];
	int upx = lx == 1 ? (a >> pos & 1) : 1;
	int upy = ly == 1 ? (b >> pos & 1) : 1;
	int t = (m >> pos & 1);
	ll ans = 0;
	for (int x = 0; x <= upx; x++) {
    
    
		for (int y = 0; y <= upy; y++) {
    
    
			if (x ^ y != (n >> pos & 1)) continue;
			ans += dfs(pos - 1,v1 * 2 + t + x - y,v2 * 2 + t + y - x,lx && x == upx,ly && y == upy);
		}
	}
	if (!lx && !ly) dp[pos][v1 + 1][v2 + 1] = ans;
	return ans;
}
int main() {
    
    
	scanf("%d",&t);
	while (t--) {
    
    
		scanf("%lld%lld%lld%lld",&a,&b,&n,&m);
		memset(dp,-1,sizeof dp);
		printf("%lld\n",dfs(62,0,0,1,1));
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_41997978/article/details/108822769