class Solution {
public int[] maximumWeight(List<List<Integer>> intervals) {
int n = intervals.size();
// Step 1: Sort by right endpoint, then by left, then by weight, then by original index
int[][] arr = new int[n][4]; // [l, r, weight, originalIndex]
for (int i = 0; i < n; i++) {
arr[i][0] = intervals.get(i).get(0); // l
arr[i][1] = intervals.get(i).get(1); // r
arr[i][2] = intervals.get(i).get(2); // weight
arr[i][3] = i; // original index
}
Arrays.sort(arr, (a, b) -> a[1] != b[1] ? a[1] - b[1] :
a[0] != b[0] ? a[0] - b[0] :
a[2] != b[2] ? a[2] - b[2] : a[3] - b[3]);
// Step 2: DP
// dp[k][i] = {maxScore, lexSmallestIndices[]}
// choosing k intervals from first i intervals
// We choose up to 4 intervals
// State: dp[i][k] = best {score, indices[]} using k intervals, last ending at arr[i]
// For space, store score and index array
long[][] dpScore = new long[n][5]; // dpScore[i][k] = max score with k intervals, i-th as last
int[][][] dpIdx = new int[n][5][]; // dpIdx[i][k] = lex smallest indices
// Initialize
for (long[] row : dpScore) Arrays.fill(row, -1);
// Helper: compare two index arrays lexicographically
// Returns true if a < b
for (int i = 0; i < n; i++) {
// k=1: just pick interval i
dpScore[i][1] = arr[i][2];
dpIdx[i][1] = new int[]{arr[i][3]};
// Binary search for last interval j where arr[j][1] < arr[i][0]
// (strictly less, since sharing boundary = overlapping)
int lo = 0, hi = i - 1, prev = -1;
while (lo <= hi) {
int mid = (lo + hi) / 2;
if (arr[mid][1] < arr[i][0]) {
prev = mid;
lo = mid + 1;
} else {
hi = mid - 1;
}
}
for (int k = 2; k <= 4; k++) {
// Option 1: don't pick i → handled when we pick best across all i
// Option 2: pick i + best (k-1) from prev
if (prev >= 0) {
// Find best dp[j][k-1] for j in 0..prev
// (We'll handle this with prefix best arrays)
}
}
}
// Better approach: prefix best dp
// best[k] = {bestScore, bestIndices} for exactly k chosen intervals so far
long[] bestScore = new long[5];
int[][] bestIdx = new int[5][];
Arrays.fill(bestScore, -1);
// dp over sorted intervals
// For each interval i, we can extend any best[k-1] where last interval doesn't overlap
// Store per-position best
long[][] posScore = new long[n + 1][5];
int[][][] posIdx = new int[n + 1][5][];
for (long[] row : posScore) Arrays.fill(row, -1);
for (int i = 0; i < n; i++) {
// Binary search: rightmost j < i where arr[j][1] < arr[i][0]
int lo = 0, hi = i - 1, prev = -1;
while (lo <= hi) {
int mid = (lo + hi) / 2;
if (arr[mid][1] < arr[i][0]) { prev = mid; lo = mid + 1; }
else hi = mid - 1;
}
int p = prev + 1; // prefix index (1-based): best among 0..prev
for (int k = 1; k <= 4; k++) {
// Don't pick i: carry forward
posScore[i + 1][k] = posScore[i][k];
posIdx[i + 1][k] = posIdx[i][k];
// Pick i: need best (k-1) from 0..prev
long prevS = (k == 1) ? 0 : posScore[p][k - 1];
int[] prevI = (k == 1) ? new int[0] : posIdx[p][k - 1];
if (k == 1 || prevS >= 0) {
long newScore = prevS + arr[i][2];
int[] newIdx = append(prevI, arr[i][3]);
Arrays.sort(newIdx);
if (posScore[i + 1][k] < newScore ||
(posScore[i + 1][k] == newScore && isLess(newIdx, posIdx[i + 1][k]))) {
posScore[i + 1][k] = newScore;
posIdx[i + 1][k] = newIdx;
}
}
}
}
// Find best across all k=1..4
long best = -1;
int[] ans = null;
for (int k = 1; k <= 4; k++) {
long s = posScore[n][k];
int[] idx = posIdx[n][k];
if (s < 0) continue;
if (s > best || (s == best && isLess(idx, ans))) {
best = s;
ans = idx;
}
}
return ans == null ? new int[0] : ans;
}
private int[] append(int[] arr, int val) {
int[] res = new int[arr.length + 1];
System.arraycopy(arr, 0, res, 0, arr.length);
res[arr.length] = val;
return res;
}
// Returns true if a is lexicographically smaller than b
private boolean isLess(int[] a, int[] b) {
if (b == null) return true;
if (a == null) return false;
for (int i = 0; i < Math.min(a.length, b.length); i++) {
if (a[i] != b[i]) return a[i] < b[i];
}
return a.length < b.length;
}
}
TIME COMPLEXITY
SPACE COMPLEXITY