题解: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\) 使得:

  1. \(x \operatorname{xor} y = u\)
  2. \(2 \times (x \operatorname{add} y) = v - u\)

所以 \(v - u\) 定为偶数。

观察可得如下性质:

  1. \(x \operatorname{xor} y\)\(u\) 的某一位为 \(1\) 时,\(x \operatorname{add} y\)\(\frac{v - u}{2}\) 的这一位一定是 \(0\)
  2. \(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;
}
posted @ 2025-11-11 15:51  QEDQEDQED  阅读(12)  评论(1)    收藏  举报