ABC050D/ARC066D Xor Sum
题目大意
可表为 \((a \xor b, a + b)\) 的二元组有多少个?
\(a, b\) 满足下列约束条件:
① \(a, b\) 是非负整数;
② \(a + b \le N\),\(N\) 是给定的正整数且 \(N \le 10^{18}\) 。
我的思考
考虑 \(a \xor b\) 的二进制表示,对其进行数位 DP。
问题转化成
$ 0 \le x < 2^k \(
\) 0 \le y < 2^k \(
\) x + y \le M\(
\)k, M$ 给定且 $ 2^k \le M < 2^{k + 1}$ 。
二元组 \((x \xor y, x + y)\) 有多少种不同取值?
这个问题并不容易,思路至此中断。
参考题解
https://blog.csdn.net/just_sort/article/details/54288233
Key observation
可以给 \(a, b\) 增加一个约束而不改变原题目的解:
③ \(a\) 的每个二进制位都不大于 \(b\) 的对应二进制位。
在这三条约束下,可以证明 \((a, b) \mapsto (a \xor b, a + b)\) 是单射。
证明:设 \((a_1, b_1) \ne (a_2, b_2)\) 。若 \(a_1 \xor b_1 = a_2 \xor b_2\) 则必有某个二进制位,在此位上某一组的值是 \((0,0)\),另一组的值是 \((1, 1)\) 。考虑满足此条件的最高位,易见 \(a_1+ b_1 \ne a_2 + b_2\) 。证毕。
至此问题化为在上述三个约束下,满足 \(a + b \le N\) 的二元组 \((a, b)\) 有多少个?
解法一(DP,top-down with memoization)
令 \(f(N)\) 表示所求,考虑 \(a, b\) 的最低位(即权重为 \(2^0\) 的位),\(a, b\) 在此位上的取值有三种情况:\((0, 0)\)、\((0, 1)\)、\((1, 1)\);得到递推
边界条件:
$ f(0) = 1, f(1) = 2 $
问题:算出 \(f(N)\) 需要计算多少个状态?
据说状态数在 \((\log N)^2\) 的级别,我不能证明。
解法二(DP)
DP 状态
dp[i][ j ][k]:\(a, b\) 的后 \(i\) 位已经确定,\(a + b\) 的后 \(i\) 位和 \(N\) 的后 \(i\) 位的大小关系是 \(j\)(j = 0 代表小于,j = 1 代表等于,j = 2 代表大于),\(k\) 表示 \(a\) 的后 \(i\) 位和 \(b\) 的后 \(i\) 位相加是否会向第 \(i + 1\) 位产生进位(k = 0, 1)。
转移方式
对于状态 dp[i][ j ][k],枚举 \(a, b\) 在第 \(i + 1\) 位上的取值,转移到状态 dp[i+1][ j' ][k' ]。
\(N\) 的二进制第 \(i + 1\) 位是 0
dp[i][ j ][0] -- (0, 0) --> dp[i+1][ j ][0]
dp[i][ j ][0] -- (0, 1) --> dp[i+1][2][0]
dp[i][ j ][0] -- (1, 1) --> dp[i+1][ j ][1]
dp[i][ j ][1] -- (0, 0) --> dp[i+1][2][0]
dp[i][ j][1] -- (0, 1) --> dp[i+1][ j][1]
dp[i][ j][1] -- (1, 1) --> dp[i+1][ 2][1]
\(N\) 的二进制第 \(i + 1\) 位是 1
dp[i][ j][0] -- (0, 0) --> dp[i+1][0][0]
dp[i][ j][0] -- (0, 1) --> dp[i+1][ j][0]
dp[i][ j][0] -- (1, 1) --> dp[i+1][0][1]
dp[i][ j][1] -- (0, 0) --> dp[i+1][ j][0]
dp[i][ j][1] -- (0, 1) --> dp[i+1][0][1]
dp[i][ j][1] -- (1, 1) --> dp[i+1][ j][1]
代码
https://atcoder.jp/contests/abc050/submissions/8191466
DP 状态优化
上述 dp 数组的第二维可以优化。以 j = 0 表示小于等于,j = 1 表示大于;或者以 j = 0 表示小于等于,j = 1 表示不计大小关系(即小于、等于、大于三种情况之和)。 按前一种定义,dp[i][j][k] 的转移方式为
\(N\) 的二进制第 \(i + 1\) 位是 0
dp[i][ j ][0] -- (0, 0) --> dp[i+1][ j ][0]
dp[i][ j ][0] -- (0, 1) --> dp[i+1][1][0]
dp[i][ j ][0] -- (1, 1) --> dp[i+1][ j ][1]
dp[i][ j ][1] -- (0, 0) --> dp[i+1][1][0]
dp[i][ j][1] -- (0, 1) --> dp[i+1][ j][1]
dp[i][ j][1] -- (1, 1) --> dp[i+1][1][1]
\(N\) 的二进制第 \(i + 1\) 位是 1
dp[i][ j][0] -- (0, 0) --> dp[i+1][0][0]
dp[i][ j][0] -- (0, 1) --> dp[i+1][ j][0]
dp[i][ j][0] -- (1, 1) --> dp[i+1][0][1]
dp[i][ j][1] -- (0, 0) --> dp[i+1][ j][0]
dp[i][ j][1] -- (0, 1) --> dp[i+1][0][1]
dp[i][ j][1] -- (1, 1) --> dp[i+1][ j][1]
参考代码
https://atcoder.jp/contests/abc050/submissions/8192354
按后一种定义,dp[i][j][k] 的转移方式为
\(N\) 的二进制第 \(i + 1\) 位是 0
dp[i][ j ][0] -- (0, 0) --> dp[i+1][ j ][0]
dp[i][1][0] -- (0, 1) --> dp[i+1][1][0]
dp[i][ j][0] -- (1, 1) --> dp[i+1][ j ][1]
dp[i][1][1] -- (0, 0) --> dp[i+1][1][0]
dp[i][ j][1] -- (0, 1) --> dp[i+1][ j][1]
dp[i][1][1] -- (1, 1) --> dp[i+1][1][1]
\(N\) 的二进制第 \(i + 1\) 位是 1
dp[i][1][0] -- (0, 0) --> dp[i+1][0][0]
dp[i][1][0] -- (1, 1) --> dp[i+1][0][1]
dp[i][ j][0] -- (0, 1) --> dp[i+1][ j][0]
dp[i][ j][1] -- (0, 0) --> dp[i+1][ j][0]
dp[i][ j][1] -- (1, 1) --> dp[i+1][ j][1]
dp[i][1][1] -- (0, 1) --> dp[i+1][0][1]
这种状态定义的好处是转移路径少,坏处是状态转移过程容易写错。
另一种 DP
仍按上述思路,下面介绍官方题解给出的 DP 方法。这种方法的复杂度比较清楚,并且其思想可以用于求解更为一般的数位 DP 问题。
DP 状态
dp[i][ j ]:\(a + b\) 的二进制表示的第 \(i\) 位及以上的部分(确切地说,权值大于等于 \(2^{i}\) 的那些位)已经确定且不考虑第 \(i\) 位以下部分,\(a + b\) 的二进制第 \(i\) 位及以上的部分(换言之,\(a + b\) 已经确定的部分)与 \(N\) 的二进制第 \(i\) 位及以上的部分的差是 \(j\)(亦即 (N >> i) - ((a + b) >> i) == j
)的情况有多少种。
举例言之,N = 10101。a + b = 1(符号 * 表示暂不考虑这些位上的值)属于状态 dp[4][0],1 - 1 = 0;a + b = 0 属于状态 dp[4][1],1 - 0 = 1;a + b = 00*** 属于状态 dp[3][2],10 - 00 = 2;a + b = 10*** 属于状态 dp[3][0],10 - 10 = 0;a + b = 100** 属于状态 dp[2][1],101 - 100 = 1;000** 属于状态 dp[2][5],101 - 000 = 5。
对于状态 dp[i][ j ],注意到当 \(j \ge 2\) 时,\(a + b\) 的二进制第 \(0\) 到 \(i - 1\) 位可以任意选取,共有 \(3^{i}\) 种情况。所以 \(j \ge 2\) 的状态可以用 \(j = 2\) 表示,因此 \(j\) 可以只取 \(0, 1, 2\) 这三个值。另外,由于只有 \(j = 0, 1\) 的状态需要转移,在编程实现时,dp 数组的第二维取 2 即可。
转移方式
只有 dp[i][0] 和 dp[i][1] 需要转移;看 \(N\) 的二进制第 \(i - 1\) 位上是多少,枚举 \(a, b\) 的二进制第 \(i - 1\) 位。
(1)\(N\) 的二进制第 \(i - 1\) 位上是 0
dp[i][0] -- (0, 0) --> dp[i - 1][0]
dp[i][1] -- (0, 0) --> dp[i - 1][2]
dp[i][1] -- (0, 1) --> dp[i - 1][1]
dp[i][1] -- (1, 1) --> dp[i - 1][0]
(2)\(N\) 的二进制第 \(i - 1\) 位上是 1
dp[i][0] -- (0, 0) --> dp[i - 1][1]
dp[i][0] -- (0, 1) --> dp[i - 1][0]
dp[i][1] -- (0, 0) --> dp[i - 1][2]
dp[i][1] -- (0, 1) --> dp[i - 1][2]
dp[i][1] -- (1, 1) --> dp[i - 1][1]
边界条件
由于 \(N \le 10^{18}\) 而 \(\log 10^{18} \approx 59.79\),故边界条件可取为 dp[60][0] = 1, dp[60][1] = 0 。
复杂度
时间复杂度 \(O(\log N)\),空间复杂度 \(O(\log N)\) 。
代码
https://atcoder.jp/contests/arc066/submissions/8186588