[Codeforces] Too Many Segments

Smaller input size:

https://codeforces.com/contest/1249/problem/D1

Big input size:

https://codeforces.com/contest/1249/problem/D2

 

Given n segments on a X coordinate line. Segments can intersect, lie inside each other or even coincide. The i-th segment is [Li, Ri], Li <= Ri and it covers all integers that is in [Li, Ri]. An integer is called bad if it is covered by strictly more than k segments. Remove the minimum number of segments so that there are no bad integers at all. Output the minimum number of segments you need to remove and which segments you need to remove.

 

Greedy solution: If an integer is covered by more than k segments, remove segments with biggest right bound until there are no more than k segements covering this integer. This strategy provides a better chance of fewer segments covering bigger integers that will be checked later. 

For small input size,  a brute force solution is sufficient. 

For big input size, we need to come up with a faster solution.

1. Sort all segments by their left bound L.

2. Create a max priority queue that's used to find the segment with the biggest right bound in case of having to remove segments.

3. Create a counter array for each integer: cnt[i]. It represents at integer i, the number of segments that end on i - 1, and have been added to the max pq. 

4. Define a counter variable called cover. For each integer i, it represents how many segments that are in the max pq covers i. 

5. Starting from the smallest to the biggest integer, do the following: 

(a). as long as the current segment s[j] is possible to cover the current integer i (s[j].L <= i), add s[j] to the max pq. Increment cover by 1. Decrement cnt[s[j].R + 1] by 1, so when checking integer s[j].R + 1, we can exclude the segments that end on s[j].R.

(b). At this point the variable cover does not necessarily reflect the actual segment counts that cover i. We must exclude the segments that end on i - 1. Do this step for each integer we check. In other words, this cover variable has excluded all segments that are in the max pq but do not cover i. For example, if there are 2 segments that are in max pq but does not cover i, then when checking integers smaller than i, we've already excluded segments whose end is smaller than i - 1. So when checking i, we just need to exclude segments whose end IS i - 1.

(c). After (b), we have the correct covering segment count for i. If it is bigger than k, keep polling from the max pq and decrement this covering count by 1 until it is <= k. Each time a segment is polled, we must increment cnt[polledSegment.R + 1] by 1. This represents there is one fewer segment that ends on polledSegment.R because we just removed polledSegment. 

(d). Each time a segment is removed, add its index to a list. 

 

The runtime of this optimized solution is: O(N) + O(S * log S).

O(N): N is the max integer. i increments from 0 to N.

O(S * log S): S is the max number of segments. Sorting is O(S * log S). Each segment is added to the max pq at most once and removed from the max pq at most once. So all heap operations are O(2 * S * log S).

 

import java.io.*;
import java.util.*;

public class TooManySegmentsHard {
    private static class Segment {
        int l, r, idx;
        Segment(int l, int r, int idx) {
            this.l = l;
            this.r = r;
            this.idx = idx;
        }
        int getL() {
            return l;
        }
        int getR() {
            return r;
        }
        int getIdx() { return idx; }
    }
    public static void main(String[] args) {
        FastScanner in = new FastScanner(System.in);
        PrintWriter out = new PrintWriter(System.out);
        int n = in.nextInt();
        int k = in.nextInt();
        int[] cnt = new int[(int)2e5 + 1];
        Segment[] s = new Segment[n];
        int minInt = (int)2e5 + 5, maxInt = -1;
        for(int i = 0; i < n; i++) {
            int l = in.nextInt() - 1;
            int r = in.nextInt() - 1;
            minInt = Math.min(l, minInt);
            maxInt = Math.max(r, maxInt);
            s[i] = new Segment(l, r, i);
        }

        Arrays.sort(s, Comparator.comparingInt(Segment::getL));

        PriorityQueue<Segment> q = new PriorityQueue<>(Comparator.comparingInt(Segment::getR).reversed());

        int segIdx = 0, cover = 0;
        List<Integer> removeIndices = new ArrayList<>();
        for(int i = minInt; i <= maxInt; i++) {
                while (segIdx < n && s[segIdx].getL() <= i) {
                    q.add(s[segIdx]);
                    cnt[s[segIdx].getR() + 1]--;
                    segIdx++;
                    cover++;
                }
                cover += cnt[i];
                while (cover > k) {
                    Segment removeSeg = q.poll();
                    removeIndices.add(removeSeg.getIdx());
                    cover--;
                    cnt[removeSeg.getR() + 1]++;
                }
        }

        out.println(removeIndices.size());
        for (int i : removeIndices) {
            out.print((i + 1) + " ");
        }
        out.close();

        }
    }
}

 

posted @ 2019-10-25 04:56  Review->Improve  阅读(534)  评论(0编辑  收藏  举报