1# from titan_pylib.data_structures.set.sorted_multiset import SortedMultiset 2# https://github.com/tatyam-prime/SortedSet/blob/main/SortedMultiset.py 3importmath 4frombisectimportbisect_left,bisect_right 5fromtypingimportGeneric,Iterable,Iterator,TypeVar,Optional 6 7T=TypeVar("T") 8 9 10classSortedMultiset(Generic[T]): 11BUCKET_RATIO=16 12SPLIT_RATIO=24 13 14def__init__(self,a:Iterable[T]=[])->None: 15"Make a new SortedMultiset from iterable. / O(N) if sorted / O(N log N)" 16a=list(a) 17n=self.size=len(a) 18ifany(a[i]>a[i+1]foriinrange(n-1)): 19a.sort() 20num_bucket=int(math.ceil(math.sqrt(n/self.BUCKET_RATIO))) 21self.a=[ 22a[n*i//num_bucket:n*(i+1)//num_bucket] 23foriinrange(num_bucket) 24] 25 26def__iter__(self)->Iterator[T]: 27foriinself.a: 28forjini: 29yieldj 30 31def__reversed__(self)->Iterator[T]: 32foriinreversed(self.a): 33forjinreversed(i): 34yieldj 35 36def__eq__(self,other)->bool: 37returnlist(self)==list(other) 38 39def__len__(self)->int: 40returnself.size 41 42def__repr__(self)->str: 43return"SortedMultiset"+str(self.a) 44 45def__str__(self)->str: 46s=str(list(self)) 47return"{"+s[1:len(s)-1]+"}" 48 49def_position(self,x:T)->tuple[list[T],int,int]: 50"return the bucket, index of the bucket and position in which x should be. self must not be empty." 51fori,ainenumerate(self.a): 52ifx<=a[-1]: 53break 54return(a,i,bisect_left(a,x)) 55 56def__contains__(self,x:T)->bool: 57ifself.size==0: 58returnFalse 59a,_,i=self._position(x) 60returni!=len(a)anda[i]==x 61 62defcount(self,x:T)->int: 63"Count the number of x." 64returnself.index_right(x)-self.index(x) 65 66defadd(self,x:T)->None: 67"Add an element. / O(√N)" 68ifself.size==0: 69self.a=[[x]] 70self.size=1 71return 72a,b,i=self._position(x) 73a.insert(i,x) 74self.size+=1 75iflen(a)>len(self.a)*self.SPLIT_RATIO: 76mid=len(a)>>1 77self.a[b:b+1]=[a[:mid],a[mid:]] 78 79def_pop(self,a:list[T],b:int,i:int)->T: 80ans=a.pop(i) 81self.size-=1 82ifnota: 83delself.a[b] 84returnans 85 86defdiscard(self,x:T)->bool: 87"Remove an element and return True if removed. / O(√N)" 88ifself.size==0: 89returnFalse 90a,b,i=self._position(x) 91ifi==len(a)ora[i]!=x: 92returnFalse 93self._pop(a,b,i) 94returnTrue 95 96deflt(self,x:T)->Optional[T]: 97"Find the largest element < x, or None if it doesn't exist." 98forainreversed(self.a): 99ifa[0]<x:100returna[bisect_left(a,x)-1]101102defle(self,x:T)->Optional[T]:103"Find the largest element <= x, or None if it doesn't exist."104forainreversed(self.a):105ifa[0]<=x:106returna[bisect_right(a,x)-1]107108defgt(self,x:T)->Optional[T]:109"Find the smallest element > x, or None if it doesn't exist."110forainself.a:111ifa[-1]>x:112returna[bisect_right(a,x)]113114defge(self,x:T)->Optional[T]:115"Find the smallest element >= x, or None if it doesn't exist."116forainself.a:117ifa[-1]>=x:118returna[bisect_left(a,x)]119120def__getitem__(self,i:int)->T:121"Return the i-th element."122ifi<0:123forainreversed(self.a):124i+=len(a)125ifi>=0:126returna[i]127else:128forainself.a:129ifi<len(a):130returna[i]131i-=len(a)132raiseIndexError133134defpop(self,i:int=-1)->T:135"Pop and return the i-th element."136ifi<0:137forb,ainenumerate(reversed(self.a)):138i+=len(a)139ifi>=0:140returnself._pop(a,~b,i)141else:142forb,ainenumerate(self.a):143ifi<len(a):144returnself._pop(a,b,i)145i-=len(a)146raiseIndexError147148defindex(self,x:T)->int:149"Count the number of elements < x."150ans=0151forainself.a:152ifa[-1]>=x:153returnans+bisect_left(a,x)154ans+=len(a)155returnans156157defindex_right(self,x:T)->int:158"Count the number of elements <= x."159ans=0160forainself.a:161ifa[-1]>x:162returnans+bisect_right(a,x)163ans+=len(a)164returnans