Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Multisets in Python

I am working on this problem on CSES, Traffic Lights:

There is a street of length 𝑥 whose positions are numbered 0,1,…,𝑥. Initially there are no traffic lights, but 𝑛 sets of traffic lights are added to the street one after another.

Your task is to calculate the length of the longest passage without traffic lights after each addition.

Input

The first input line contains two integers 𝑥 and 𝑛: the length of the street and the number of sets of traffic lights.

Then, the next line contains n integers 𝑝1,𝑝2,…,𝑝𝑛: the position of each set of traffic lights. Each position is distinct.

Output

Print the length of the longest passage without traffic lights after each addition.

Constraints

  • 1 ≤ 𝑥 ≤ 109
  • 1 ≤ 𝑛 ≤ 2⋅105
  • 0 < 𝑝𝑖 < 𝑥

Example

Input:

8 3
3 6 2

Output:

5 3 3

So to effectively solve a problem like this, I need a data structure in Python similar to a list but the search and deletion of elements need to be O(1) or more like a data structure similar to sets but I need to be able to insert multiple same elements and also preserve order. My code for the problem is:

from collections import defaultdict
from bisect import bisect_right , insort
x , n = list(map(int  , input().split()))
arr = list(map(int , input().split()))
lens = defaultdict(int)
lens[x] = 1
lights = [0,x]
for ele in arr:
    idx = bisect_right(lights , ele)
    to_be_removed = lights[idx] - lights[idx-1]
    lens[to_be_removed] -= 1
    lens[lights[idx]-ele] += 1
    lens[ele-lights[idx-1]] += 1
    insort(lights , ele)
    print(max([x for x in lens.keys() if lens[x]])  , end =" ") 

However this code is slow. There is a data structure called multi-sets in c++. However couldn't find a similar data structure in python. Any help appreciated.

like image 630
eyah Avatar asked Sep 28 '26 16:09

eyah


1 Answers

The data structure you have for lens is like a multiset, also available as Counter. The part of your algorithm that is the bottle neck in terms of time complexity, is this:

max([x for x in lens.keys() if lens[x]]) 

This is an operation with linear time complexity, and so it makes the algorithm quadratic.

To improve on that part of the algorithm, I'd suggest using a heap. There is heapq which provides a min heap implementation. As you actually need a max heap, you'd just feed it with negative lengths.

Secondly, insort also has a linear time complexity (although using less time than the max() expression above). You can improve on this by using a self-balancing search tree implementation, for which there is no standard library, but there are libraries that provide sorted lists such as sortedcontainers.

Here is how you can adjust your code to implement those two ideas:

from collections import defaultdict
from heapq import heappush, heappop
from sortedcontainers import SortedList

x , n = list(map(int  , input().split()))
arr = list(map(int , input().split()))

lens = defaultdict(int)
lens[x] = 1
lights = SortedList([0, x])  # For faster insertion
heap = [-x]  # Put total width also in a heap
for ele in arr:
    idx = lights.bisect_right(ele)
    to_be_removed = lights[idx] - lights[idx-1]
    lens[to_be_removed] -= 1

    # Add widths to the heap when they are the only occurrences
    right = lights[idx]-ele
    if lens[right] == 0:
        heappush(heap, -right)
    lens[right] += 1

    left = ele-lights[idx-1]
    if lens[left] == 0:
        heappush(heap, -left)
    lens[left] += 1

    # Remove the largest width as long as it no longer represents a segment
    while lens[-heap[0]] == 0:
        heappop(heap)
    
    # The add method is O(logn)
    lights.add(ele)
    # Just output the largest width in the heap
    print(-heap[0], end = " ")
like image 68
trincot Avatar answered Oct 01 '26 04:10

trincot



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!