1. 题目背景与核心思路解析
这道来自Codeforces Round 1072 (Div. 3)的D题"Unfair Game",考察了二进制操作与组合数学的结合应用。题目定义了一个将正整数归零的特殊操作规则:
- 若x为偶数:执行x ← x/2(即右移一位)
- 若x为奇数:执行x ← x-1(即清除最低位的1)
给定n=2^d和步数限制k,我们需要计算区间[1,n]内有多少数字的归零操作次数会超过k。这个问题的巧妙之处在于将数字的二进制表示与操作步骤建立了直接对应关系。
1.1 二进制视角的操作分析
从二进制角度看,每个操作步骤实际上是在处理数字的比特位:
- 当最低位为0时(偶数),右移操作相当于消除这个0,消耗1步
- 当最低位为1时(奇数),先减1清除这个1,再右移,共消耗2步
- 特殊情况:最高位的1只需减1操作,消耗1步
通过这种对应关系,我们可以推导出将一个数字s归零所需的总步数公式:
Move(s) = MSB(s) + popcount(s)
其中:
- MSB(s)表示s的最高有效位位置(从0开始计数)
- popcount(s)表示s的二进制中1的个数
1.2 解题思路拆解
要解决这个问题,我们需要:
- 预处理组合数C(i,j),用于后续计算
- 对于每个测试用例(n,k):
- 计算d=log2(n)
- 统计满足Move(s)≤k的数字个数cnt
- 答案为n-cnt
- 统计cnt时需要考虑:
- 数字n本身是否满足条件
- 对于每个可能的最高位i,计算对应区间内满足条件的数字数量
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 组合数学与算法实现
2.1 组合数预处理
在解决这个问题时,我们需要频繁使用组合数C(i,j)(即从i个元素中取j个的组合数)。预处理这些组合数可以显著提高计算效率。
cpp复制ll C[35][35]; // 组合数表,35足够覆盖题目范围
void Init() {
for(int i = 0; i <= 32; i++) {
C[i][0] = 1; // 任何数的C(i,0)=1
for(int j = 1; j <= i; j++) {
C[i][j] = C[i
