题解:AT_arc066_b [ABC050D] Xor Sum
一种挺难写的数位 DP 做法。
思路
题目要求一个点对 \((u, v)\) 使得存在 \(x, y\),\(x \operatorname{xor} y = u, x + y = v\)。
首先转化一下题意,由于异或运算是不进位的加法运算,那么根据丰富的位运算经验可得:\(x + y = x \operatorname{xor} y + 2 \times (x \operatorname{add} y)\)。
这时问题就变成了对于一个点对 \((u, v)\) 是否存在 \(x, y\) 使得:
- \(x \operatorname{xor} y = u\)
- \(2 \times (x \operatorname{add} y) = v - u\)
所以 \(v - u\) 定为偶数。
观察可得如下性质:
- 当 \(x \operatorname{xor} y\) 即 \(u\) 的某一位为 \(1\) 时,\(x \operatorname{add} y\) 即 \(\frac{v - u}{2}\) 的这一位一定是 \(0\)。
- 当 \(u\) 的某一位是 \(0\) 时,\(\frac{v - u}{2}\) 的这一位可能是 \(0\) 或 \(1\)。
那么此时 \(u \operatorname{add} \frac{v - u}{2}=0\)。
得到这一点就可以开始数位 DP 了。
数位 DP 部分
具体的从高位到低位枚举二进制位转移。
由于需要知道 \(u, v\) 是否贴着 \(n\) 这个上界,所以需要开设两维状态 \(lim_1, lim_2\)。
又因为 \(v \geq u\),于是再加一维是否满足 \(u = v\)。
但是枚举这个减法确实是有点恶心,由于减法是有借位的,所以可以设 \(add\) 为下一位(更低的位)借上来的大小,然后枚举的时候判断一下是不是满足条件。
最后由于要满足 \(u \operatorname{add} \frac{v - u}{2}=0\),所以需要 \(\frac{v - u}{2}\) 的当前位,也就是 \(v - u\) 的上一位(更高的位)。
跑完后检查一下 \(v - u\) 是不是偶数,是不是没有借位了。
综上需要一个 \(6\) 维 DP,分别维护当前是哪一位,\(u, v\) 是否贴着上界,是否有 \(u = v\),下一位借上来的位,\(v - u\) 的上一位。
共有 \(2^5 \log n\) 种状态,转移有 \(2^3\) 种。
总复杂度 \(\mathcal{O}(256\log n)\)。
代码
#include <iostream>
#include <cstring>
using namespace std;
const int N = 70, mod = 1e9 + 7;
using ll = long long;
int dp[N][2][2][2][2][2];
int dfs(ll num, int n, int lim1, int lim2, int add, int comp, int lb)
{
if (n == -1)
{
if (!(add == 0 && lb == 0)) return 0;
return 1;
}
int &res = dp[n][lim1][lim2][add][comp][lb];
if (res != -1) return res;
res = 0;
int d = (num >> n) & 1ll;
int o1 = lim1 ? d : 1, o2 = lim2 ? d : 1;
for (int o = 0; o <= o1; o++)
{
for (int p = 0; p <= o2; p++)
{
for (int _add = 0; _add <= 1; _add++)
{
if (comp && p > o) continue;
if (add == 0 && (o - p - _add) < 0) continue;
if (add == 1 && (o - p - _add) >= 0) continue;
if (lb && p) continue;
(res += dfs(num, n - 1, lim1 && o == o1, lim2 && p == o2, _add, comp && (p == o), (o - p - _add + 2) % 2)) %= mod;
}
}
}
return res;
}
signed main()
{
ios :: sync_with_stdio(false), cin.tie(0), cout.tie(0);
ll x; cin >> x;
memset(dp, -1, sizeof(dp));
cout << dfs(x, 63, 1, 1, 0, 1, 0) << '\n';
return 0;
}

浙公网安备 33010602011771号