题目分析
(1)注意到任意进制的数都有一个性质,即如果数
A
A
A第
i
i
i位有一个非0,那么即便数
B
B
B所有低于
i
i
i的位置都是最大值,有
A
>
B
A > B
A>B。因此我们想到这个二进制数的某一位是0/1可能会影响它的排名。
(2)直接地,对于最高的二进制位,如果为1,那么就大于了所有最高二进制位为0的数字,我们只需判断目标位置
i
i
i是否满足当前位置为1的结果即可。随后,如果当前位置为1,进行更新,考虑剩下的位置里最多有
m
i
n
(
n
−
1
,
L
−
1
)
min(n - 1, L - 1)
min(n−1,L−1)个1,我们将
n
n
n与
L
L
L均减一,同时由于已经将一部分的数“比”了下去,只需要考虑最高位是1的数的情况,需要把最高位不是1的数减去,即将
i
i
i减掉一部分。即寻找剩下的位里排名一定的数
(3)如果当前位置不是1,即不用做处理,因为仍然能够填1的数量只与
n
n
n,
L
L
L有关,同时比目标数小的此位也是0,因此仍然在我们考虑之中,且没有重复考虑。
(4)考虑如何判断对前
i
i
i位而言,第
i
i
i位恰好是0的数字的数量。使用dp维护
f
[
i
]
[
j
]
f[i][j]
f[i][j]代表前
i
i
i位恰好有
j
j
j个1的数的数量,转移方程容易想到。而所求只需要分别考虑前
i
i
i位有
j
j
j个1的数字,对每一种情况而言数量即
f
[
i
−
1
]
[
j
]
f[i - 1][j]
f[i−1][j],即前
i
−
1
i-1
i−1位就有
j
j
j个1的数的数量。(可能这个组合数学也能做)
代码
#include <iostream>
using ll = long long;
const int N = 35;
ll n, L, I;
ll f[N][N];//f[i][j]表示前i位,恰好含有j个1的数字的数量
int vis[N];//即第i位是否是1
int main(){
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout.tie(nullptr);
std::cin >> n >> L >> I;
f[0][0] = 1;
for(int i = 1; i <= n; ++i){
f[i][0] = 1;
}
for(int i = 1; i <= n; ++i){
for(int j = 1; j <= i; ++j){
f[i][j] = f[i - 1][j] + f[i - 1][j - 1];
//std::cout << i << " " << j << " " << f[i][j] << '\n';
}
}
for(ll i = n; i >= 1; --i){//考虑第i位设置成1的情况
//分别计算当L=j时,有多少个数字第i位是0
ll sum = 1;
for(int j = 1; j <= std::min(L, i); ++j){
sum += f[i - 1][j];
//std::cout << i << " " << j << " " << f[i][j] << " " << f[i - 1][j] << '\n';
}
//std::cout << i << " " << sum << " " << I << '\n';
if(sum <= I - 1){
vis[i] = 1;
I -= sum;
--n;
--L;
}
std::cout << vis[i];
}
return 0;
}
5884



被折叠的 条评论
为什么被折叠?



