zoukankan      html  css  js  c++  java
  • 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) e (a_2, b_2)$ 。若 $a_1 xor b_1 = a_2 xor b_2$ 则必有某个二进制位,在此位上某一组的值是 $(0,0)$,另一组的值是 $(1, 1)$ 。考虑满足此条件的最高位,易见 $a_1+ b_1 e 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(N) = f(N / 2) + f((N - 1) / 2) + f( (N - 2) / 2) $$

    边界条件:
    $ 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

    References

    https://qiita.com/259_Momone/items/86e90d17e4efe3b22433

  • 相关阅读:
    KMP算法的理解和代码实现
    关于线程死锁
    PAT1018
    PAT1059
    PAT1009
    PAT1006
    PAT1005
    PAT1004
    PAT1002
    PAT
  • 原文地址:https://www.cnblogs.com/Patt/p/11729108.html
Copyright © 2011-2022 走看看