小欧的等差数列

[题目链接](https://www.nowcoder.com/practice/7c29bf99543040efa8e493b803050bdc)

思路

给定等差数列 ,每次可以选两个数 ,若 是整数,就把它加入集合。求集合最终最大元素个数。

核心观察:最小可达步长

不妨将所有元素平移,令首项为 ,集合变为 (平移不影响中点运算)。

取两个元素 ,中点为 。要求它是整数,即 是偶数。

  • 是奇数,则需要 为偶数(同奇偶),中点 ,仍是 的整数倍,不产生新元素。
  • 是偶数,则 恒为偶数,中点 ,步长缩小为

反复对新集合继续操作,每次 含因子 时步长就可以再减半。设 为奇数,-adic 赋值),则最终步长缩至 (奇数后无法再减半)。

推导答案

最终集合覆盖区间 中所有 的整数倍:

$$

特殊情况

  • :集合最多 个元素。
  • :所有元素相同,答案

样例验证

。答案

原集合 ,取相邻元素中点得 ,最终集合 ,共 个,正确。

代码

#include <bits/stdc++.h>
using namespace std;
int main(){
    long long n, a, d;
    cin >> n >> a >> d;
    if(n <= 1 || d == 0){
        cout << 1 << endl;
        return 0;
    }
    if(d < 0) d = -d;
    long long v = 0;
    long long dd = d;
    while(dd % 2 == 0){
        dd /= 2;
        v++;
    }
    cout << (n - 1) * (1LL << v) + 1 << endl;
    return 0;
}
import java.util.Scanner;
public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        long n = sc.nextLong(), a = sc.nextLong(), d = sc.nextLong();
        if (n <= 1 || d == 0) {
            System.out.println(1);
            return;
        }
        if (d < 0) d = -d;
        long v = 0, dd = d;
        while (dd % 2 == 0) { dd /= 2; v++; }
        System.out.println((n - 1) * (1L << v) + 1);
    }
}
n, a, d = map(int, input().split())
if n <= 1 or d == 0:
    print(1)
else:
    if d < 0:
        d = -d
    v = 0
    dd = d
    while dd % 2 == 0:
        dd //= 2
        v += 1
    print((n - 1) * (1 << v) + 1)
const readline = require('readline');
const rl = readline.createInterface({ input: process.stdin });
rl.on('line', (line) => {
    let [n, a, d] = line.trim().split(' ').map(Number);
    if (n <= 1 || d === 0) { console.log(1); return; }
    if (d < 0) d = -d;
    let v = 0, dd = d;
    while (dd % 2 === 0) { dd = Math.floor(dd / 2); v++; }
    console.log((n - 1) * Math.pow(2, v) + 1);
});

复杂度

  • 时间复杂度:,只需计算 中因子 的个数。
  • 空间复杂度: