1621 LeetCode problem solution
LeetCode problem link: 1621. Number of Sets of K Non-Overlapping Line Segments
LeetCode 1621 problem description
Medium level problem.
Given n points on a 1-D plane, where the ith point from 0 to n - 1 is at x = i, find the number of ways to draw exactly k non-overlapping line segments such that each segment covers two or more points.
The endpoints of each segment must have integral coordinates.
The k line segments do not have to cover all n points, and they are allowed to share endpoints.
Return the number of ways to draw k non-overlapping line segments.
Since the answer can be very large, return it modulo 109 + 7.
Example 1:
Input: n = 4, k = 2
Output: 5
Explanation: The two line segments are shown in red and blue. The image above shows the 5 different ways {(0,2),(2,3)}, {(0,1),(1,3)}, {(0,1),(2,3)}, {(1,2),(2,3)}, {(0,1),(1,2)}.
Example 2:
Input: n = 3, k = 1
Output: 3
Explanation: The three possible segments are (0, 1), (0, 2), and (1, 2).
Example 3:
Input: n = 30, k = 7
Output: 796297179
Explanation: The total number of possible ways to draw 7 line segments is 3796297200. Taking this number modulo 109 + 7 gives us 796297179.
Constraints:
2 <= n <= 10001 <= k <= n - 1
LeetCode 1621 problem: solution explanation
This problem can be solved using Combinatorics.
We need to choose exactly k non-overlapping line segments from n points.
A segment is determined by two endpoints, so k segments have 2 * k endpoints. The difficulty is that neighboring segments are allowed to share an endpoint.
For example:
[0, 2] and [2, 3]are valid because the two segments only meet at point 2.
The main idea is to transform the endpoints so that shared endpoints become different positions. After this transformation, the problem becomes choosing 2 * k different positions from n + k - 1 positions.
Therefore, the answer is:
C(n + k - 1, 2 * k)Step 1: Describe the segment endpoints
Suppose the k segments are:
[s1, e1], [s2, e2], ..., [sk, ek]For every segment, the start must be smaller than the end:
s1 < e1
s2 < e2
...
sk < ekThe segments cannot overlap, but they can share endpoints.
Therefore:
0 <= s1 < e1 <= s2 < e2 <= ... <= sk < ek <= n - 1For example:
[0, 2], [2, 3]gives:
0 < 2 <= 2 < 3The important part is:
e1 <= s2and not:
e1 < s2because e1 and s2 may represent the same point.
Step 2: Transform shared endpoints
If all endpoints were different, we could simply choose 2 * k different points.
But shared endpoints make this impossible directly.
To remove this problem, we shift the endpoints of each next segment by one additional position.
For the first segment:
z1 = s1
z2 = e1For the second segment:
z3 = s2 + 1
z4 = e2 + 1For the third segment:
z5 = s3 + 2
z6 = e3 + 2In general, for segment i:
z(2i - 1) = si + i - 1
z(2i) = ei + i - 1Now all transformed values are strictly increasing:
z1 < z2 < z3 < z4 < ... < z(2k)Consider two segments that share an endpoint:
e1 = s2 = 2Before the transformation:
e1 = 2
s2 = 2After the transformation:
z2 = e1 = 2
z3 = s2 + 1 = 3The shared point becomes two different positions.
This allows us to treat all 2 * k transformed endpoints as distinct.
Step 3: Count the available transformed positions
Originally, the points are:
0, 1, 2, ..., n - 1The last segment is shifted by:
k - 1Therefore, the largest transformed endpoint can be:
(n - 1) + (k - 1)which is:
n + k - 2So the transformed positions are:
0, 1, 2, ..., n + k - 2There are:
n + k - 1possible positions.
We need to choose exactly:
2 * kof them.
Therefore:
answer = C(n + k - 1, 2 * k)Here, C(N, R) represents a combination - the number of ways to choose R elements from N elements without considering their order.
Step 4: Example
For:
n = 4
k = 2we have:
N = n + k - 1
N = 4 + 2 - 1
N = 5and:
R = 2 * k
R = 4Therefore:
C(5, 4) = 5This means there are 5 ways to choose 4 transformed endpoints from 5 positions.
These correspond exactly to the 5 valid sets of two non-overlappingsegments.
Step 5: Calculate the combination efficiently
We only need to calculate one combination:
C(N, R)where:
N = n + k - 1
R = 2 * kCombinations have the property:
C(N, R) = C(N, N - R)Therefore, we use:
R = min(R, N - R);For this problem:
N - R = n + k - 1 - 2k = n - k - 1so we actually need only:
R = min(2 * k, n - k - 1)For example:
C(5, 4) = C(5, 1)Instead of performing 4 iterations, we only need 1.
This does not change the result, but reduces the number of calculations.
We calculate the combination multiplicatively:
C(N, R) = N * (N - 1) * ... * (N - R + 1)
---------------------------------
1 * 2 * ... * RBecause division cannot be performed directly under modulo 109 + 7, we use modular inverses.
We precompute modular inverses only up to R:
inv[i] = MOD - (MOD / i) * inv[MOD % i] % MOD;Then we calculate the combination:
result = result * (N - R + i) % MOD;
result = result * inv[i] % MOD;LeetCode 1621 problem: complexity
After:
R = min(R, N - R);we have:
R = min(2 * k, n - k - 1)We precompute modular inverses from 1 to R and calculate the combination in another R iterations.
Time complexity: O(R)
Space complexity: O(R)
where:
R = min(2 * k, n - k - 1)In the worst case, both are O(n).
Pattern: Combinatorics
Technique: Endpoint Transformation, Binomial Coefficient, Modular Arithmetic
LeetCode 1621 C++ solution
class Solution {
public:
static constexpr long long MOD = 1'000'000'007;
int numberOfSets(int n, int k)
{
int N = n + k - 1;
int R = 2 * k;
R = min(R, N - R);
vector<long long> inv(R + 1);
if(R >= 1) inv[1] = 1;
for(int i = 2; i <= R; i++)
inv[i] = MOD - (MOD / i) * inv[MOD % i] % MOD;
long long result = 1;
for(int i = 1; i <= R; i++)
{
result = result * (N - R + i) % MOD;
result = result * inv[i] % MOD;
}
return result;
}
};LeetCode 1621 Java solution
class Solution {
static final long MOD = 1_000_000_007L;
public int numberOfSets(int n, int k) {
int N = n + k - 1;
int R = 2 * k;
R = Math.min(R, N - R);
long[] inv = new long[R + 1];
if(R >= 1) inv[1] = 1;
for(int i = 2; i <= R; i++)
inv[i] = MOD - (MOD / i) * inv[(int)(MOD % i)] % MOD;
long result = 1;
for(int i = 1; i <= R; i++) {
result = result * (N - R + i) % MOD;
result = result * inv[i] % MOD;
}
return (int)result;
}
}LeetCode 1621 JavaScript solution
var numberOfSets = function(n, k) {
const MOD = 1000000007n;
const N = n + k - 1;
let R = 2 * k;
R = Math.min(R, N - R);
const inv = new Array(R + 1).fill(0n);
if(R >= 1) inv[1] = 1n;
for(let i = 2; i <= R; i++) {
const bi = BigInt(i);
inv[i] = MOD - (MOD / bi) * inv[Number(MOD % bi)] % MOD;
}
let result = 1n;
for(let i = 1; i <= R; i++) {
result = result * BigInt(N - R + i) % MOD;
result = result * inv[i] % MOD;
}
return Number(result);
};LeetCode 1621 TypeScript solution
function numberOfSets(n: number, k: number): number {
const MOD = 1000000007n;
const N: number = n + k - 1;
let R: number = 2 * k;
R = Math.min(R, N - R);
const inv: bigint[] = new Array(R + 1).fill(0n);
if(R >= 1) inv[1] = 1n;
for(let i = 2; i <= R; i++) {
const bi = BigInt(i);
inv[i] = MOD - (MOD / bi) * inv[Number(MOD % bi)] % MOD;
}
let result: bigint = 1n;
for(let i = 1; i <= R; i++) {
result = result * BigInt(N - R + i) % MOD;
result = result * inv[i] % MOD;
}
return Number(result);
}LeetCode 1621 Python solution
class Solution:
MOD = 10**9 + 7
def numberOfSets(self, n: int, k: int) -> int:
N = n + k - 1
R = 2 * k
R = min(R, N - R)
inv = [0] * (R + 1)
if R >= 1: inv[1] = 1
for i in range(2, R + 1):
inv[i] = (self.MOD - (self.MOD // i) * inv[self.MOD % i] % self.MOD)
result = 1
for i in range(1, R + 1):
result = result * (N - R + i) % self.MOD
result = result * inv[i] % self.MOD
return result