Rearrange Sticks With K Visible
Unsigned Stirling numbers DP
Stirling numbers of the first kind. Pure combinatorial DP.
Problem
There are n uniquely-sized sticks in a row. You look from the left; a stick is visible if no taller stick stands before it.
Return the number of permutations of n sticks such that exactly k sticks are visible from the left, modulo 10^9 + 7.
Intuition
Focus on the shortest stick (height 1). It can only be visible if it is placed first (leftmost). Otherwise, it hides behind any taller stick to its left.
- If stick 1 is placed first: the remaining
n - 1sticks must producek - 1visible sticks. That isf(n-1, k-1). - If stick 1 is placed in any of the other
n - 1positions: it is invisible, and the remainingn - 1sticks must produce exactlykvisible sticks. That is(n - 1) * f(n-1, k).
Recurrence: f(n, k) = f(n-1, k-1) + (n-1) * f(n-1, k).
This is exactly the unsigned Stirling numbers of the first kind |s(n, k)|. Base cases: f(0, 0) = 1, f(n, 0) = 0 for n > 0, f(0, k) = 0 for k > 0.
Approach
Brute Permutation
O(n!)O(n)Generate all permutations, count visible sticks in each, tally those with exactly k. Only for tiny n.
impl Solution {
pub fn rearrange_sticks(n: i32, k: i32) -> i32 {
let n = n as usize;
let k = k as usize;
let mut perm: Vec<usize> = (1..=n).collect();
let mut count = 0u64;
loop {
let vis = Self::visible(&perm);
if vis == k {
count += 1;
}
if !Self::next_perm(&mut perm) {
break;
}
}
(count % 1_000_000_007) as i32
}
fn visible(p: &[usize]) -> usize {
let mut mx = 0;
let mut cnt = 0;
for &x in p {
if x > mx {
cnt += 1;
mx = x;
}
}
cnt
}
fn next_perm(a: &mut [usize]) -> bool {
let n = a.len();
if n <= 1 { return false; }
let mut i= n - 2;
while i < n && a[i] >= a[i + 1] {
if i == 0 { return false; }
i -= 1;
}
let mut j = n - 1;
while a[j] <= a[i] { j -= 1; }
a.swap(i, j);
a[i + 1..].reverse();
true
}
}function rearrangeSticks(n: number, k: number): number {
const perm = Array.from({ length: n }, (_, i) => i + 1);
let count = 0;
const visible = (p: number[]): number => {
let mx = 0, cnt = 0;
for (const x of p) { if (x > mx) { cnt++; mx = x; } }
return cnt;
};
const nextPerm = (a: number[]): boolean => {
let i = a.length - 2;
while (i >= 0 && a[i] >= a[i + 1]) i--;
if (i < 0) return false;
let j = a.length - 1;
while (a[j] <= a[i]) j--;
[a[i], a[j]] = [a[j], a[i]];
let l = i + 1, r = a.length - 1;
while (l < r) { [a[l], a[r]] = [a[r], a[l]]; l++; r--; }
return true;
};
do { if (visible(perm) === k) count++; } while (nextPerm(perm));
return count % 1_000_000_007;
}Stirling DP
O(n · k)O(n · k)Fill the f(n, k) table bottom-up with modular arithmetic.
impl Solution {
pub fn rearrange_sticks(n: i32, k: i32) -> i32 {
const MOD: i64 = 1_000_000_007;
let n = n as usize;
let k = k as usize;
if k > n { return 0; }
let mut dp = vec![vec![0i64; k + 1]; n + 1];
dp[0][0] = 1;
for i in 1..=n {
for j in 1..=k.min(i) {
dp[i][j] = (dp[i - 1][j - 1] + (i as i64 - 1) * dp[i - 1][j]) % MOD;
}
}
dp[n][k] as i32
}
}function rearrangeSticks(n: number, k: number): number {
const MOD = 1_000_000_007n;
if (k > n) return 0;
const dp: bigint[][] = Array.from({ length: n + 1 }, () =>
new Array(k + 1).fill(0n),
);
dp[0][0] = 1n;
for (let i = 1; i <= n; i++) {
for (let j = 1; j <= Math.min(k, i); j++) {
dp[i][j] = (dp[i - 1][j - 1] + BigInt(i - 1) * dp[i - 1][j]) % MOD;
}
}
return Number(dp[n][k]);
}Trace
n = 3, k = 2. Build the DP table:
| i | j=0 | j=1 | j=2 |
|---|---|---|---|
| 0 | 1 | 0 | 0 |
| 1 | 0 | 1 | 0 |
| 2 | 0 | 1 | 1 |
| 3 | 0 | 2 | 3 |
dp[3][2] = 3.
Edge cases
- k = n — only sorted ascending. Answer is 1.
- k = 1 — only the tallest stick is visible. The tallest must be first; the rest can be in any order =
(n-1)!. Equivalently|s(n,1)| = (n-1)!. - k = 0 — impossible for
n > 0. Answer is 0. - Large n, k —
n = 1000, k = 500is within theO(nk)DP.
Takeaway
"How many permutations have property P?" often reduces to a classical combinatorial recurrence. Here, the "visible sticks" question maps exactly to unsigned Stirling numbers of the first kind — a well-studied family that also counts permutations by cycle count.
The focus-on-the-smallest-element trick generalizes: whenever elements are distinguishable by size, reasoning about the extremal element's placement often yields a clean two-case recurrence.