题目
数列 \(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;
}