Xor Sum AtCoder - 2272

本文探讨了一道复杂的DP题目,通过分析a+b与a^b的关系,将问题转化为寻找a+b≤n中ab的组合数量。采用递归方式,结合二进制理解,给出了详细的解析与代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


Time limit : 2sec / Memory limit : 256MB

Score : 600 points

Problem Statement

You are given a positive integer N. Find the number of the pairs of integers u and v (0≦u,vN)such that there exist two non-negative integers a and b satisfying a xor b=u and a+b=v. Here, xor denotes the bitwise exclusive OR. Since it can be extremely large, compute the answer modulo 109+7.

Constraints

  • 1≦N≦1018

Input

The input is given from Standard Input in the following format:

N

Output

Print the number of the possible pairs of integers u and v, modulo 109+7.


Sample Input 1

Copy

3

Sample Output 1

Copy

5

The five possible pairs of u and v are:

  • u=0,v=0 (Let a=0,b=0, then 0 xor 0=0, 0+0=0.)

  • u=0,v=2 (Let a=1,b=1, then 1 xor 1=0, 1+1=2.)

  • u=1,v=1 (Let a=1,b=0, then 1 xor 0=1, 1+0=1.)

  • u=2,v=2 (Let a=2,b=0, then 2 xor 0=2, 2+0=2.)

  • u=3,v=3 (Let a=3,b=0, then 3 xor 0=3, 3+0=3.)


Sample Input 2

Copy

1422

Sample Output 2

Copy

52277

Sample Input 3

Copy

1000000000000000000

Sample Output 3

Copy

787014179

题目链接

题目大意:给出一个数字n,寻找两个数字uv,使得对于0<=u,v<=n,有a+b=u,a^b=v,求uv的对数

题解:是一道dp题,由于不是很擅长dp加上逻辑能力欠缺,看这道题看了大半天……是的,我看了大半天题解才看懂。

由于a+b = (a&b)<<1 + a^b,故可知a^b定然小于a+b,故本题可以化为寻找对于a+b<=n中ab的个数。

首先可以使用暴力的手段得知,当n=0的时候,答案为1,n=1的时候,答案为2。对于a与b(化作二进制来看),两者的末位和有0,1,2三种情况,设a+b=x,令a = a1*2+a2, b = b1*2+b2,a2+b2即为两者的末位和,又有x<=n,故a1+a2<=(n-a1-a2)/2,可以得出递推式 dp[x] = dp[x/2] + dp[(x-1)/2] + dp[(x-2)/2];

代码如下

#include<iostream>
#include<cstdio>
#include<map>
#include<sstream>
#include<cstring>
#include<algorithm>
#include<string>
using namespace std;
const long long maxn = 1e18+7;
const int mod = 1e9+7;
map<long long, long long>dp;
long long func(long long x)
{
    if(dp[x])return dp[x];
    return dp[x] = (func(x/2) + func((x-1)/2) + func((x-2)/2))%mod;
}

int main()
{
    long long n;
    cin >> n;
    dp[0] = 1;
    dp[1] = 2;
    cout << func(n) << endl;
    return 0;
}

 

<think>嗯,用户让我解析这段Java代码,特别是int lsb = ...这一行。首先,我得回忆一下这个算法的整体思路。题目是找出数组中只出现一次的两个数字,其他数字都出现两次。这种情况下,通常会用异或操作,因为相同的数字异或会抵消,结果为0。但如果有两个不同的单独数字,异或的结果会是这两个数的异或值,所以需要进一步处理。 那代码的大致流程应该是这样的:首先,对所有数进行异或,得到xorsum,这个xorsum实际上就是那两个单独数字的异或结果。因为其他数字成对出现,异或后为0。接下来的问题是如何把这两个数字分开。这时候需要找到xorsum中的一个二进制位,这个位在两个单独数字中一个是1,另一个是0。这样就能将原数组分成两组,每组各包含一个单独数字,然后分别异或处理。 现在关键点在于如何找到这样的一个二进制位。代码中用了xorsum & (-xorsum)来获取最低有效位(LSB),也就是最右边的1所在的位。但这里有个特殊情况判断,如果xorsum是Integer.MIN_VALUE,即二进制1000...000,这时候xorsum本身就是负数,取负数的话可能会溢出?或者是因为在这种情况下,xorsum & (-xorsum)的结果是否正确? 比如,当xorsum是Integer.MIN_VALUE时,-xorsum在Java中会溢出,因为int的范围是-2^312^31-1,所以Integer.MIN_VALUE的绝对值超过了最大正数,所以-xorsum的结果还是Integer.MIN_VALUE?这时候xorsum & (-xorsum)就等于xorsum本身,所以这种情况下直接取xorsum。否则的话,用xorsum & (-xorsum)来得到LSB。 这一步的意义是为了找到两个单独数字不同的最低有效位,这样可以根据这一位将数组分成两部分。然后对这两部分分别进行异或,最终得到两个单独的数字。 那用户的问题主要是这一步为什么要这么做,以及为什么要处理Integer.MIN_VALUE的情况。可能用户对位运算中的负数处理不太清楚,或者对溢出情况有疑问。需要解释清楚补码表示下的负数,以及如何用x & -x来得到最低有效位的原理。 可能还需要举例子说明。比如,假设xorsum是某个数,比如6(二进制0110),那么-xorsum在补码中是1010(假设4位的话),然后x & -x得到0010,即最低位的1。这时候就能用这个位来区分两个不同的数字。 但当xorsum是Integer.MIN_VALUE时,即二进制1后面31个0,那么-xorsum在补码中是无法表示的,因为int的范围到不了+2^31,所以这时候的-xorsum会溢出,导致结果还是Integer.MIN_VALUE本身?或者可能这时候的计算结果是否正确? 比如,在Java中,对于Integer.MIN_VALUE取负数,实际上还是Integer.MIN_VALUE,因为溢出。所以在xorsum为Integer.MIN_VALUE时,xorsum & (-xorsum)的结果就是xorsum本身,这时候作为掩码是否正确?这个时候只有最高位是1,所以根据这个掩码分组的话,其他数的该位是否为1会被分组。这时候是否正确? 比如,假设两个单独的数字是x和y,它们的异或结果是x ^ y = Integer.MIN_VALUE,即最高位不同。这时候用这个掩码的话,x和y在这一位一个是1,另一个是0。所以这时候的分组是正确的,可以正确地将两个数分开。因此,这一步的处理是正确的,不需要特殊处理?或者可能当xorsum是Integer.MIN_VALUE时,xorsum & (-xorsum)就等于xorsum本身,所以这时候直接使用即可,但为什么代码中要单独处理这个情况? 可能是在这种情况下,xorsum & (-xorsum)的结果会因为溢出而不正确?或者是否在Java中,当xorsum是Integer.MIN_VALUE时,-xorsum仍然是Integer.MIN_VALUE(因为溢出),所以这时候进行&运算的结果就是xorsum本身。这时候,掩码是正确的,因为最低有效位就是最高位。所以这个条件判断可能只是为了防止溢出带来的问题? 或者可能当xorsum是Integer.MIN_VALUE的时候,xorsum & (-xorsum)的计算会得到正确的掩码,所以不需要特殊处理,但代码中作者可能为了保险起见,或者在某些情况下可能出错,所以直接使用xorsum? 比如,假设xorsum是Integer.MIN_VALUE,那么它的二进制是10000000 00000000 00000000 00000000。在Java中,-xorsum的计算是取反加1,即补码运算。原数的补码是它本身(因为是负数)。取反的话,得到01111111 ... 1111,加1得到10000000 ... 0000,所以-xorsum等于xorsum本身。这时候xorsum & (-xorsum)的结果就是xorsum。这时候,lsb的值就是xorsum本身,也就是最高位的1。这时候,用这个掩码来分割数组是正确的,因为两个单独的数字在这一位上是不同的。因此,这个条件判断是否是多余的? 可能作者考虑到当xorsum是Integer.MIN_VALUE时,-xorsum会导致溢出,从而使得xorsum & (-xorsum)的结果仍然是xorsum,这在逻辑上是正确的。所以,这个时候的条件判断是否必要? 例如,假设xorsum是Integer.MIN_VALUE,那么xorsum & (-xorsum)的结果等于xorsum,而这时候xorsum & (-xorsum)的结果确实是正确的掩码(唯一的1位),所以此时条件判断是否多余? 可能作者在这里是为了处理可能的情况,即当xorsum为Integer.MIN_VALUE时,直接使用xorsum本身作为掩码,而不用做位运算,因为在这种情况下,-xorsum会导致溢出,但位运算的结果仍然正确。或者,是否在某些情况下,当xorsum是Integer.MIN_VALUE时,xorsum & (-xorsum)的行为会有问题? 或者,可能作者在这里的考虑是,当xorsum是Integer.MIN_VALUE时,xorsum & (-xorsum)的结果还是xorsum,而其他情况下,用xorsum & (-xorsum)来得到最低有效位。所以条件判断可能只是为了避免潜在的溢出问题,但实际上在这种情况下,位运算的结果是正确的,所以条件判断可能并非必须,但作者出于谨慎添加了。 所以,这一步的目的是找到xorsum中最右侧的1的位置,作为分组的依据。例如,如果xorsum6(二进制0110),那么xorsum & (-xorsum)得到0010,即第二个bit位。这样,数组中所有数的这个bit位为1的分为一组,剩下的为另一组。这样就能确保两个单独的数被分到不同的组中,而其他相同的数会被分到同一组,从而异或后消去。 总结来说,lsb这一步是为了找到两个单独数不同的最低有效位,从而将数组分成两组,每组各包含一个单独数,然后通过异或找出这两个数。而处理Integer.MIN_VALUE的情况可能是为了避免溢出或者确保正确性。</think>我们来逐步解析这段代码,特别是关键步骤 `int lsb = ...` 的作用。 --- ### **代码整体思路** 题目要求:**在数组中找出仅出现一次的两个数字,其余数字均出现两次。** 代码核心思路: 1. **异或性质**:相同数字异或结果为 $0$,不同数字异或结果为 $1$。因此,对所有数字异或后,最终结果 `xorsum` 等于两个目标数字的异或值。 2. **分组**:通过 `xorsum` 的某一位将数组分为两组,每组包含一个目标数字,其他数字成对出现,最终通过异或消去。 --- ### **关键代码解析** #### **1. 计算异或总和** ```java int xorsum = 0; for (int num : nums) { xorsum ^= num; ``` - **目的**:得到两个目标数字的异或值 `xorsum`。例如,若目标数字是 $a$ 和 $b$,则 `xorsum = a ^ b`。 --- #### **2. 确定最低有效位(LSB)** ```java int lsb = (xorsum == Integer.MIN_VALUE ? xorsum : xorsum & (-xorsum)); ``` - **作用**:找到 `xorsum` 中最右侧的 $1$ 的位置(即最低有效位),用于分组。 - **原理**: - `x & (-x)` 在二进制中会保留最右侧的 $1$,其余位设为 $0$。例如: - 若 $x = 6$(二进制 $0110$),则 $-x = 1010$(补码表示),$x \& (-x) = 0010$。 - **特殊情况**:当 `xorsum` 是 `Integer.MIN_VALUE`(二进制 $1000\cdots0000$),其补码表示为自身,直接取 `xorsum` 本身作为掩码。 --- #### **3. 分组异或** ```java int type1 = 0, type2 = 0; for (int num : nums) { if ((num & lsb) != 0) { type1 ^= num; } else { type2 ^= num; } } ``` - **步骤**: 1. 根据 `lsb` 将数组分为两组: - 第 $1$ 组:数字在 `lsb` 对应位为 $1$。 - 第 $2$ 组:数字在 `lsb` 对应位为 $0$。 2. 每组分别异或,结果即为两个目标数字。 --- ### **示例说明** 假设数组为 $[1, 2, 1, 3, 2, 5]$,目标数字是 $3$ 和 $5$: 1. **计算 `xorsum`**: $3$ ^ $5 = 6$(二进制 $0110$)。 2. **确定 `lsb`**: $6 \& (-6) = 0010$(二进制)。 3. **分组**: - 第 $1$ 组(`lsb` 位为 $1$):$[2, 3, 2]$ → 异或结果为 $3$。 - 第 $2$ 组(`lsb` 位为 $0$):$[1, 1, 5]$ → 异或结果为 $5$。 --- ### **总结** - **`lsb` 的作用**:通过异或结果的某一位差异,将数组分为两组,确保两个目标数字被分离。 - **特殊处理 `Integer.MIN_VALUE`**:防止补码运算溢出导致掩码错误,直接使用其本身作为掩码。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值