ABC050D/ARC066D Xor Sum

题目链接

题目大意

可表为 (a\xorb,a+b) 的二元组有多少个?
a,b 满足下列约束条件:
a,b 是非负整数;
a+bNN 是给定的正整数且 N1018

我的思考

考虑 a\xorb 的二进制表示,对其进行数位 DP。
问题转化成

$ 0 \le x < 2^k 0 \le y < 2^k x + y \le Mk, M 2^k \le M < 2^{k + 1}$ 。
二元组 (x\xory,x+y) 有多少种不同取值?

这个问题并不容易,思路至此中断。

参考题解

https://blog.csdn.net/just_sort/article/details/54288233

Key observation
可以给 a,b 增加一个约束而不改变原题目的解:
a 的每个二进制位都不大于 b 的对应二进制位。

在这三条约束下,可以证明 (a,b)(a\xorb,a+b) 是单射。

证明:设 (a1,b1)(a2,b2) 。若 a1\xorb1=a2\xorb2 则必有某个二进制位,在此位上某一组的值是 (0,0),另一组的值是 (1,1) 。考虑满足此条件的最高位,易见 a1+b1a2+b2 。证毕。

至此问题化为在上述三个约束下,满足 a+bN 的二元组 (a,b) 有多少个?

解法一(DP,top-down with memoization)

f(N) 表示所求,考虑 a,b 的最低位(即权重为 20 的位),a,b 在此位上的取值有三种情况:(0,0)(0,1)(1,1);得到递推

f(N)=f(N/2)+f((N1)/2)+f((N2)/2)

边界条件:
f(0)=1,f(1)=2

问题:算出 f(N) 需要计算多少个状态?

据说状态数在 (logN)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 位及以上的部分(确切地说,权值大于等于 2i 的那些位)已经确定且不考虑第 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 ],注意到当 j2 时,a+b 的二进制第 0i1 位可以任意选取,共有 3i 种情况。所以 j2 的状态可以用 j=2 表示,因此 j 可以只取 0,1,2 这三个值。另外,由于只有 j=0,1 的状态需要转移,在编程实现时,dp 数组的第二维取 2 即可。

转移方式

只有 dp[i][0] 和 dp[i][1] 需要转移;看 N 的二进制第 i1 位上是多少,枚举 a,b 的二进制第 i1 位。

(1)N 的二进制第 i1 位上是 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 的二进制第 i1 位上是 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]

边界条件

由于 N1018log101859.79,故边界条件可取为 dp[60][0] = 1, dp[60][1] = 0 。

复杂度

时间复杂度 O(logN),空间复杂度 O(logN)

代码

https://atcoder.jp/contests/arc066/submissions/8186588

References

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

posted @   Pat  阅读(310)  评论(0编辑  收藏  举报
编辑推荐:
· 从二进制到误差:逐行拆解C语言浮点运算中的4008175468544之谜
· .NET制作智能桌面机器人:结合BotSharp智能体框架开发语音交互
· 软件产品开发中常见的10个问题及处理方法
· .NET 原生驾驭 AI 新基建实战系列:向量数据库的应用与畅想
· 从问题排查到源码分析:ActiveMQ消费端频繁日志刷屏的秘密
阅读排行:
· C# 13 中的新增功能实操
· Ollama本地部署大模型总结
· 【杭电多校比赛记录】2025“钉耙编程”中国大学生算法设计春季联赛(4)
· langchain0.3教程:从0到1打造一个智能聊天机器人
· 用一种新的分类方法梳理设计模式的脉络
历史上的今天:
2016-10-28 English Snippets
2016-10-28 Ubuntu 使用笔记
2016-10-28 在CentOS上安装Sublime Text
点击右上角即可分享
微信分享提示