题目

数列 \(S_n\) 如下定义:

  • \(S_1\) 是一个只含有 \(1\)\(1\) 的长度为 \(1\) 的数列
  • \(S_n\) 是按照 \(S_{n-1}, n, S_{n-1}\) 的顺序连接得到

下面以 \(S_2, S_3\) 为例:

\(S_2\) 是由 \(S_1, 2, S_1\) 连接得到,因为 \(S_1\)\(1\) 个数 \(1\),所以 \(S_2\)\(1, 2, 1\)
\(S_3\) 是由 \(S_2, 3, S_2\) 连接得到,所以 \(S_3\)\(1, 2, 1, 3, 1, 2, 1\)

给出 \(n\)\(k\),输出 \(S_n\) 的第 \(k\) 个数。

限制:

  • \(1 \leqslant n \leqslant 64\)\(1 \leqslant k \leqslant 2^{64}-1\)\(k\) 不超过 \(S_n\) 的长度。

注意 \(k\) 可能超过 \(64\) 位有符号整数类型范围,需要使用 \(64\) 位无符号整数类型。

算法分析

要找 \(S_n\) 的第 \(k\) 个数可化为 \(S_{n-1}\) 中的某数的问题
用递归

f(n, k) 表示返回 \(Sn\) 中第 \(k\) 个数的值

Sn-1    n    Sn-1
 前     中    后

L[n-1]\(S_{n-1}\) 的长度
\(k \leqslant L[n-1]\),则说明在前面,返回 f(n-1, k)
\(k \geqslant L[n-1]+2\),则说明在后面,返回 f(n-1, k-L[n-1]-1)
\(k = L[n-1]+1\),则说明在正中间,返回 \(n\)

L[i] 表示 \(S_i\) 的长度
递推式:\(L[i] = 2\cdot L[i-1] + 1\)
通项:\(L[i] = 2^i - 1\)

代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)

using namespace std;
using ull = unsigned long long;

int main(){
    int n; 
    ull k;
    cin >> n >> k;
    
    vector<ull> l(n+1);
    rep(i, n) l[i+1] = 2*l[i] + 1;
    
    auto f = [&](auto& f, int n, ull k) -> int {
        if (k <= l[n-1]) return f(f, n-1, k);
        if (k >= l[n-1]+2) return f(f, n-1, k-l[n-1]-1); 
        if (k == l[n-1]+1) return n;
    };
    
    int ans = f(f, n, k);
    cout << ans << '\n';
    
    return 0;
}