题目链接:LuoguP3048
做法:\(dp\) + 递归。
首先,我们可以发现,排名为 \(N\) 的奶牛编号位数一定不超过 \(5 \times 10^3\),故,我们可以使用位数来解决此题。
我们设 \(f_{i,j}\) 表示在前 \(i\) 位上放 \(j\) 个 \(1\) 的方案数,转移则为 \(f_{i,j} = f_{i-1,j} + f_{i - 1,j - 1}\) ,也就是分成 \(2\) 种情况:新增加进来的那位数为 \(0\) 或 \(1\) 的 \(2\) 种情况。
然后递归求解,对于第 \(step\) 个位置,我们可以选择填 \(1\) 或填 \(0\),若当前的排名大于所有长度比它小 \(1\) 且满足条件的编号的方案数,则当前位置填 \(1\) 否则则填 \(0\)。
然后由于首位必须为 \(1\),所以用个 \(opt\) 记录是否有 \(1\) 存在过,在 \(opt\) 为 \(0\) 时,就不能输出 \(0\)。
\(Code:\)
#include <bits/stdc++.h>
const int MaxN = 5e3;
const int MaxK = 10 + 10;
using namespace std;
inline int read() {
int cnt = 0, opt = 1;
char ch = getchar();
for (; ch < '0' || ch > '9'; ch = getchar())
if (ch == '-') opt = 0;
for (; ch >= '0' && ch <= '9'; ch = getchar())
cnt = (cnt << 3) + (cnt << 1) + (ch ^ 48);
return opt ? cnt : -cnt;
}//快读优化
int N, K, opt;
int f[MaxN + 10][MaxK];
inline void dfs(int x, int y, int step) {
if (! step) return;
if (x <= f[step - 1][y]) {//不选
if (opt) putchar('0');
dfs(x, y, step - 1);
}
else {//选
putchar('1'); opt = 1;
dfs(x - f[step - 1][y], y - 1, step - 1);
}
}
int main() {
N = read(), K = read();
if (K == 1) {
putchar('1');
for (int i = 1; i < N; ++ i) putchar('0');
return 0;
}//特判 K 为 1 的情况
for (int i = 0; i <= MaxN; ++ i)
f[i][0] = 1;
for (int i = 1; i <= MaxN; ++ i)
for (int j = 1; j <= K; ++ j)
f[i][j] = min((int)1e7, f[i - 1][j - 1] + f[i - 1][j]);//预处理f数组
dfs(N, K, MaxN);//递归处理
return 0;
}