网易首页 > 网易号 > 正文 申请入驻

2026-09-13:区间内的兼容数字之和Ⅰ。用go语言,有两个整数 n、k。现在要从所有正整数里挑出满足下面两个要求的数 x: 第一,x 与 n 相

0
分享至

2026-09-13:区间内的兼容数字之和Ⅰ。用go语言,有两个整数 n、k。现在要从所有正整数里挑出满足下面两个要求的数 x:

第一,x 与 n 相差不能超过 k,也就是说,n 与 x 的差的绝对值要小于或等于 k。

第二,把 n 和 x 做按位与运算后,结果必须是 0。换句话说,在二进制形式下,n 和 x 不能在同一个位置上同时都是 1。

请找出全部满足条件的 x,并把这些 x 加在一起,返回总和。

其中,按位与运算就是符号 & 所表示的运算;两个整数之间的绝对差,就是较大的数减去较小的数。

1 <= n <= 100。

1 <= k <= 100。

输入: n = 2, k = 3。

输出: 10。

解释:

兼容整数为:

x = 1,因为 abs(2 - 1) = 1 且 2 & 1 = 0。

x = 4,因为 abs(2 - 4) = 2 且 2 & 4 = 0。

x = 5,因为 abs(2 - 5) = 3 且 2 & 5 = 0。

因此,答案为 1 + 4 + 5 = 10。

题目来自力扣3954。

一、第一步:把"区间求和"改成"两个前缀之差"

题目要求的是闭区间[n-k, n+k]内、且与n按位与为 0 的正整数x之和。

但正整数不能小于 1,所以左端点要兜底:

  • • 右端点high = n + k

  • • 左端点low = max(n-k, 1)(当n-k < 1时从 1 开始)

接着定义一个前缀函数F(N)

F(N) = 所有满足 0 ≤ x < N 且 (x & n) == 0 的 x 之和。

这里刻意用左闭右开[0, N),有两个好处:

  1. 1. 区间拼接天然无缝:[low, high]的和 =F(high+1) - F(low)。注意右端点要+1,因为F不含右端点。

  2. 2. 把0也算进去了,但0 & n == 0且它对和的贡献是 0,所以丝毫不影响结果,省掉了"排除 0"的特判。

于是原问题被拆成两次calc调用再相减,这就是sumOfGoodIntegers里那一行在做的事。

二、第二步:F(N)要怎么算——把数字当二进制串,从高位往低位扫

暴力做法是枚举0 ~ N-1每个数判一次,复杂度O(N)。这份代码的思路是整体统计

xN都看成二进制串,从最高位到最低位一位一位比。扫描时始终维护这样一个状态:

当前的"前缀"(比当前位更高的那些位)已经和 N 完全一样,也就是说还没决出大小,贴着上界走。

对某一位i

  • 如果 N 的第 i 位是 0x这一位只能填 0(填 1 就超过 N 了),继续贴着上界,什么都不用统计。

  • 如果 N 的第 i 位是 1:出现分叉,产生两支:

    • 分支 A("变小"支)x这一位填0。此时x < N已经板上钉钉(更高位都相同,这一位 N 是 1、x 是 0),后面所有低位可以随便填(只要满足与 n 的与为 0)。这一整块数可以一次性用公式算出来,不用逐个枚举。

    • 分支 B("紧贴"支)x这一位填1,继续保持和 N 相等的前缀,进入下一位继续扫描。

为什么这样不重不漏?因为任意一个满足x < N的数,它与 N 的二进制比较中,必定存在唯一的一个最高位j,使得x的第j位是 0、N的第j位是 1(更高位全相同)。扫描到i = j时,走"分支 A"正好把它收进去;而它在其它位上走的是紧贴支,不会产生贡献。所以每个合法数被且只被统计一次。

三、第三步:引入"自由位"——把约束(x & n) == 0翻译掉

条件x & n == 0等价于:凡是 n 的二进制为 1 的位,x 必须为 0;而 n 为 0 的位,x 填 0、填 1 都无所谓。

所以代码定义:

  • • 取m = N的二进制位数(即bits.Len(N)),构造 m 位全 1 的掩码2^m - 1

  • freeMask = (2^m - 1) &^ n:在 m 位范围内,把所有 n 为 1 的位清掉,剩下的 1 就是"自由位"(可 0 可 1 的位);

  • freeCnt = freeMask 中 1 的个数,也就是自由位的个数。

有了自由位,"分支 A"里"后面低位随便填"就变成了一个可数的问题:低位共有freeCnt个自由位,每个 0/1 任选,一共2^freeCnt个不同的后缀

四、第四步:分支 A 一次性算出整块的和

当在第i位走分支 A(N 该位为 1、x 该位填 0)时,所有这类 x 都长成:

x = 高位固定前缀 prefix  +  低位的自由位任意组合

其中prefix是扫描过程中"紧贴支"上已经填了 1 的那些高位拼出来的数值(用一个变量prefix累积,见第六步)。

2^freeCnt个数的总和可以拆成两部分相加(因为加法可以逐位拆):

1)前缀部分的贡献
每个数都包含同一个prefix,一共有2^freeCnt个数:

贡献 = prefix × 2^freeCnt,代码写成 prefix << freeCnt。

2)后缀(自由位)部分的贡献
单独看某一个自由位 b:在所有2^freeCnt种组合中,它有恰好一半即2^(freeCnt-1)种情况取 1,所以这一位的贡献是2^b × 2^(freeCnt-1)

把所有自由位加起来:(所有自由位的 2^b 之和) × 2^(freeCnt-1)。而"所有自由位的2^b之和"恰好就是freeMask这个整数本身(因为自由位之间不重叠,二进制加法不进位)。

贡献 = freeMask × 2^(freeCnt-1),代码写成 freeMask * (1 << freeCnt >> 1)。这个式子还天然处理了 freeCnt = 0 的边界:1<<0>>1 = 0,即没有自由位时后缀贡献为 0。

两部分相加,就得到了分支 A 这一整块的总和,一次加进res

五、第五步:每一轮开头对自由位集合的"瘦身"

循环体第一件事是:

如果 n 的第 i 位是 0(说明第 i 位本来算在自由位里),就把 freeCnt 减 1,并把这一位从 freeMask 中剔除。

用意是:进入第i位的处理时,freeMask / freeCnt必须只描述"当前位 i 以下(更低位)"的自由位。因为第i位本身是"现在要决定填 0 还是填 1"的那一位,它一旦走分支 A 就已经被固定成 0 了,不能算进"后面能随便填的自由位"里。

如果n的第i位是 1,那这一位压根不在freeMask里,自然不需要剔除。

六、第六步:分支 B(继续紧贴)的处理与循环终止

走完分支 A 后,还要考虑分支 B(x这一位填 1,继续贴着 N 走)。这里有一个硬约束:

只有 n 的第 i 位是 0 时,x 这一位才能填 1。
  • • 如果n的第i位是 0:可以填 1,于是把这一位并入前缀(prefix |= 1 << i),继续下一位。

  • • 如果n的第i位是 1:x这一位必须是 0,而 N 这一位是 1,说明紧贴支不可能再产生任何x ≥ N的合法解了——所有合法解都已经在刚才的分支 A 里被算完了,直接break结束。

关于循环自然结束(一直没 break):扫描完第 0 位后,紧贴支最终得到的那个数是x = N本身。由于F(N)的区间是[0, N)N自己不该被算进去,而代码恰好从头到尾只在"分支 A"(x某位填 0 而 N 填 1)时才累加,紧贴支走到最后从不额外加N。所以左闭右开的语义被天然满足了,不需要任何收尾特判。

七、完整走一遍样例:n = 2, k = 3

1)确定区间与差分
low = max(2-3, 1) = 1high = 2+3 = 5,答案 =F(6) - F(1)

2)算F(6)N = 6 = 110₂n = 2 = 010₂

  • m = 3(6 需要 3 位),freeMask = 111₂ &^ 010₂ = 101₂ = 5freeCnt = 2prefix = 0res = 0


位 i

瘦身后的 freeMask / freeCnt

N 该位

分支 A(x 填 0)

分支 B

i=2

n 该位为 0 → 剔除第 2 位,freeMask=1(即 001₂),freeCnt=1

是 1

前缀贡献0<<1 = 0;后缀贡献1 × (2^1/2) = 1。即数集 {000,001} = {0,1},和 = 1。res=1

n 该位为 0,可填 1 → prefix = 4

i=1

n 该位为 1 → 不变,freeMask=1,freeCnt=1

是 1

前缀贡献4<<1 = 8;后缀贡献1 × 1 = 1。即数集 {100,101} = {4,5},和 = 9。res=10

n 该位为 1 → 不能填 1,break

F(6) = 10,对应合法数 {0, 1, 4, 5},和 = 0+1+4+5 = 10。✓

3)算F(1)N = 1 = 1₂

  • m = 1freeMask = 1 &^ 2 = 1freeCnt = 1

  • • i=0:n 该位为 0 → 剔除后 freeMask=0,freeCnt=0。N 该位是 1 → 分支 A 贡献0<<0 = 00 × 0 = 0,res 仍为 0;分支 B 令 prefix=1,循环结束(x=1 本身不计入)。

  • F(1) = 0(小于 1 的只有 0)。

4)相减10 - 0 = 10

与样例输出一致:兼容数是1、4、5,和为10。✓

八、复杂度分析

M = high + 1 = n + k + 1m = bits.Len(M) = ⌊log₂M⌋ + 1,也就是数字的二进制位数。

  • 时间复杂度:O(log(n + k))
    每次calc内部只有一个从m-10的单重循环,循环体里全都是 O(1) 的位运算和整数四则运算(移位、按位与、异或、加法、乘法),没有嵌套、没有递归、没有枚举所有数。调用两次calc,所以总时间是2 × O(m) = O(log(n+k))。对比一下:暴力枚举[low, high]需要 O(k) 次判断,而这个方法与 k 的大小无关,只和数字的二进制位数有关——即使 n、k 大到 10⁹ 甚至 10¹⁸,也只要循环 30~60 次(当然在 Go 的 int 下要注意溢出,本题 n,k ≤ 100 完全无压力)。

  • 额外空间复杂度:O(1)
    全程只用了m、freeMask、freeCnt、prefix、res、i这几个整型变量(外加sumOfGoodIntegers里的low、high),没有任何数组、哈希表、递归栈或动态分配的空间,占用不随输入规模变化。

一句话总结:先用前缀差分把闭区间变成两个"小于 N"的前缀问题;再按二进制从高位扫到低位,借助"自由位"把与n的与为 0 这个约束转化成"低位有多少位可任意填";每当上界某位为 1 时,就把"该位填 0、低位全自由"的那一整块数用prefix × 2^c + freeMask × 2^(c-1)一次性算出,从而在 O(log) 时间和 O(1) 空间内得到答案。

Go完整代码如下:

package main

import (
"fmt"
"math/bits"
)

// 计算小于 high 的正整数中,AND n 等于 0 的数之和
func calc(high, n int) (res int) {
m := bits.Len(uint(high))
freeMask := (1< 1 ) &^ n
freeCnt := bits.OnesCount( uint (freeMask))
prefix := 0

for i := m - 1 ; i >= 0 ; i-- {
if n>>i& 1 == 0 {
freeCnt--
freeMask ^= 1 << i
}
if high>>i& 1 > 0 {
// 这一位填 0
res += prefix << freeCnt // 前缀的贡献:后面 freeCnt 个位置,0 和 1 随便填
res += freeMask * ( 1 << freeCnt >> 1 ) // 后缀的贡献:每个 free 位置固定为 1 时,其余 freeCnt-1 个位置 0 和 1 随便填

// 这一位填 1,继续计算
if n>>i& 1 > 0 { // 这一位不能填 1
break
}
prefix |= 1 << i
}
}

return
}

func sumOfGoodIntegers(n, k int) int {
low := max(n-k, 1 )
high := n + k
return calc(high+ 1 , n) - calc(low, n)
}

func main() {
n := 2
k := 3
result := sumOfGoodIntegers(n, k)
fmt.Println(result)
}

Python完整代码如下:

# -*-coding:utf-8-*-

def calc(high, n):
res = 0
m = high.bit_length()
free_mask = ((1 << m) - 1) & ~n
free_cnt = bin(free_mask).count("1")
prefix = 0

for i in range(m - 1, -1, -1):
if ((n >> i) & 1) == 0:
free_cnt -= 1
free_mask ^= 1 << i

if ((high >> i) & 1) > 0:
# 这一位填 0
res += prefix << free_cnt
res += free_mask * ((1 << free_cnt) >> 1)

# 这一位填 1,继续计算
if ((n >> i) & 1) > 0:
break

prefix |= 1 << i

return res

def sumOfGoodIntegers(n, k):
low = max(n - k, 1)
high = n + k
return calc(high + 1, n) - calc(low, n)

def main():
n = 2
k = 3
result = sumOfGoodIntegers(n, k)
print(result)

if __name__ == "__main__":
main()

C++完整代码如下:

  



using namespace std;

// 计算小于 high 的正整数中,AND n 等于 0 的数之和
long long calc(long long high, long long n) {
if (high <= 0) return 0;

// 计算 high 的二进制位数
int m = 0;
unsigned long long uh = high;
m = 64 - __builtin_clzll(uh);

// 构造掩码:低 m 位全为 1
unsigned long long mask = (m == 64) ? ~0ULL : ((1ULL << m) - 1);
// freeMask 表示在 n 中为 0 的位(即可以自由填 1 的位)
unsigned long long freeMask = mask & ~(unsigned long long)n;
int freeCnt = __builtin_popcountll(freeMask);

long long res = 0;
long long prefix = 0;

for (int i = m - 1; i >= 0; i--) {
// 如果 n 的第 i 位是 0,则这一位是自由的,从 freeMask 中移除
if (((n >> i) & 1) == 0) {
freeCnt--;
freeMask ^= (1ULL << i);
}

if (((high >> i) & 1) > 0) {
// 当前位填 0 的情况
res += prefix << freeCnt;
res += (long long)freeMask * ((1ULL << freeCnt) >> 1);

// 当前位尝试填 1
if (((n >> i) & 1) > 0) {
// n 的这一位是 1,不能填 1,直接结束
break;
}
prefix |= (1LL << i);
}
}

return res;
}

long long sumOfGoodIntegers(long long n, long long k) {
long long low = max(n - k, 1LL);
long long high = n + k;
return calc(high + 1, n) - calc(low, n);
}

int main() {
long long n = 2;
long long k = 3;
long long result = sumOfGoodIntegers(n, k);
cout << result << endl;
return 0;
}

我们相信人工智能为普通人提供了一种“增强工具”,并致力于分享全方位的AI知识。在这里,您可以找到最新的AI科普文章、工具评测、提升效率的秘籍以及行业洞察。 欢迎关注“福大大架构师每日一题”,发消息可获得面试资料,让AI助力您的未来发展。

特别声明:以上内容(如有图片或视频亦包括在内)为自媒体平台“网易号”用户上传并发布,本平台仅提供信息存储服务。

Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.

相关推荐
热点推荐
印度小伙来中国,与中国姑娘交往2个月被踹,姑娘:我现在全身病

印度小伙来中国,与中国姑娘交往2个月被踹,姑娘:我现在全身病

悬案解密档案
2025-05-21 10:46:02
两性关系:发现了没,寿命长的男人,大多都有这五个好习惯

两性关系:发现了没,寿命长的男人,大多都有这五个好习惯

荔子言
2026-09-08 14:55:50
果然不留!高市新内阁名单出炉,林芳正出局,或扛“反高市”大旗

果然不留!高市新内阁名单出炉,林芳正出局,或扛“反高市”大旗

琴音缭绕回
2026-09-17 15:35:12
WTT球星赛冷门迭爆:国乒小将3-2掀翻世乒赛冠军,薛飞首轮崩盘,方博一轮游

WTT球星赛冷门迭爆:国乒小将3-2掀翻世乒赛冠军,薛飞首轮崩盘,方博一轮游

好乒乓
2026-09-18 00:54:14
广州突发伤人事件:最该有的保命习惯,在外不要玩手机不要戴耳机

广州突发伤人事件:最该有的保命习惯,在外不要玩手机不要戴耳机

西虹市闲话
2026-09-15 10:49:51
韩媒:日本亚运会表演出现“丰臣秀吉”引韩方强烈不满,日本亚组委回应

韩媒:日本亚运会表演出现“丰臣秀吉”引韩方强烈不满,日本亚组委回应

环球网资讯
2026-09-17 11:49:06
劳斯莱斯接亲途中发生车祸,受损严重,车主:车借给朋友接亲,车辆无保险需自费维修,修车费预计几十万元;当地派出所介入处理

劳斯莱斯接亲途中发生车祸,受损严重,车主:车借给朋友接亲,车辆无保险需自费维修,修车费预计几十万元;当地派出所介入处理

极目新闻
2026-09-17 15:54:34
国锦赛一夜2场冷门!中国军团4胜1负,张安达、霍金斯都造6-0惨案

国锦赛一夜2场冷门!中国军团4胜1负,张安达、霍金斯都造6-0惨案

小火箭爱体育
2026-09-17 22:32:35
深夜爆大瓜!匿名爆料引爆热搜:网传85花与前辈老公已离婚,约定等孩子长大再官宣

深夜爆大瓜!匿名爆料引爆热搜:网传85花与前辈老公已离婚,约定等孩子长大再官宣

火山詩话
2026-09-18 05:06:28
38岁韦雪“见光死”,生图脸大肩窄矮小,穿堆堆袜邋遢,目测一米五

38岁韦雪“见光死”,生图脸大肩窄矮小,穿堆堆袜邋遢,目测一米五

原梦叁生
2026-09-17 18:11:44
香山论坛突发意外!中方受邀贵宾被扣,总理斡旋,对方登门谢罪

香山论坛突发意外!中方受邀贵宾被扣,总理斡旋,对方登门谢罪

杜鱂手工制作
2026-09-16 16:20:14
沙特用自身遭遇证明:土耳其和巴基斯坦并不适合成为真正的忠实盟友

沙特用自身遭遇证明:土耳其和巴基斯坦并不适合成为真正的忠实盟友

寰球经纬所
2026-09-16 21:27:21
不服就干!韩国打响反华第一枪,通告全球,妄想掐断中方退路?

不服就干!韩国打响反华第一枪,通告全球,妄想掐断中方退路?

影孖看世界
2026-09-16 22:38:51
认得3个算我输!50年前的6样老物件,第1个就难倒我,最后一个村长也认不出

认得3个算我输!50年前的6样老物件,第1个就难倒我,最后一个村长也认不出

白浅娱乐聊
2026-09-17 02:34:21
贺子珍始终认定朱道来便是战乱之中失散的儿子,可朱道来后来坦言:过去他没有道出所有真相

贺子珍始终认定朱道来便是战乱之中失散的儿子,可朱道来后来坦言:过去他没有道出所有真相

唠叨说历史
2026-09-15 14:16:32
国内严控,海外严查,华人的钱流动难

国内严控,海外严查,华人的钱流动难

以希腊之名
2026-09-17 16:51:51
国家卫健委呼吁多喝白开水!最新研究:水烧开,微塑料摄入暴跌70%-80%,且还能控糖,但要静置后再喝

国家卫健委呼吁多喝白开水!最新研究:水烧开,微塑料摄入暴跌70%-80%,且还能控糖,但要静置后再喝

梅斯医学
2026-09-16 07:54:38
办不起就别办!中国队自费住酒店,泰国队机场打地铺,印度队最惨

办不起就别办!中国队自费住酒店,泰国队机场打地铺,印度队最惨

天马幸福的人生
2026-09-17 20:25:07
五百名医生已证实:维生素B12与甲钴胺的真相,最好花点时间看看

五百名医生已证实:维生素B12与甲钴胺的真相,最好花点时间看看

路医生健康科普
2026-08-03 15:25:14
体制内的饭局基本消失了

体制内的饭局基本消失了

砚田文化
2026-09-16 18:33:32
2026-09-18 06:32:49
moonfdd incentive-icons
moonfdd
福大大架构师每日一题
1462文章数 82关注度
往期回顾 全部

科技要闻

影视飓风Tim反掰iPhoneDuo被质疑

头条要闻

永和豆浆视频被指擦边:女子穿蕾丝吊带 脱黑丝袜洗澡

头条要闻

永和豆浆视频被指擦边:女子穿蕾丝吊带 脱黑丝袜洗澡

体育要闻

逆转朝鲜,国足亚运队“啃老”过关

娱乐要闻

rapper赵涛恋情曝光!带甜馨妈妈散步

财经要闻

缺钱的追觅 隐藏的债务?

汽车要闻

小鹏G9L限时售23.18万 旗舰级的配置/诱人的价格

态度原创

手机
时尚
旅游
房产
本地

手机要闻

鸿蒙7花粉Beta版有惊喜,华为Mate 60系列手机升级后新增支持眼动翻页功能

潮流之向,自有回响——浪潮音乐大赏特别企划

旅游要闻

烟花+戏剧+赛事全拉满!长江口吴淞岸线带你解锁中秋国庆滨江新玩法

房产要闻

突发!三亚安居房出台新政!

本地新闻

不止胖东来!许昌藏着半部三国史

无障碍浏览 进入关怀版