1
1

More than 1 year has passed since last update.

Pythonでお手軽MultiSet!

Posted at

はじめに

python勢が一度はぶつかるmultiset、、、

もう何も怖くない!!

背景

実装

BinaryIndexTree(BIT)を基に実装。

  • self.List
    • Setに格納される予定のある、重複のない値のリスト
  • self.size
    • self.Listの長さ
  • self.length
    • multisetの要素数
  • self.tree
    • BIT用のリスト、値の個数が入る
  • self.kvlist
    • 値をself.ListのIndexに変換する辞書(座圧とも呼ばれる)、1-Indexed

※Setに入る値の種類が分かっている前提でのみ使用可能。

class MultiSet:
    def __init__(self, List):
        self.List   = sorted(set(List))
        self.size   = len(self.List)
        self.length = 0
        self.tree   = [0] * (self.size + 1)
        self.kvlist = {j:i+1 for i,j in enumerate(self.List)}
 
    def add(self, i, x):
        i = self.kvlist[i]
        self.length += x
        while i <= self.size:
            self.tree[i] += x
            i += i & -i
            
    def bisect_left(self, w):
        if w <= 0:return 0
        x=0;r=1;n=self.size
        while r<n: r = r<<1
        while r>0:
            if x+r < n and self.tree[x+r] < w:
                w-=self.tree[x+r]
                x+=r
            r>>=1
        return x
    
    def __getitem__(self, w):
        return self.List[self.bisect_left(w)]
 
    def __len__(self):
        return self.length

使用方法

リストの長さをNとしたときO(log N)で値の追加削除参照が可能。

List = [7,3,3,5,5,5,1]
ms = MultiSet(List)

for v in List:
    # vを1個追加
    ms.add(v,1)

print([ms[i]for i in range(1, ms.size+1)]) # [1, 3, 3, 5, 5, 5, 7]

for v in List:
    # vを1個削除
    ms.add(v,-1)

print([ms[i]for i in range(1, ms.size+1)]) # []

応用編

二分探索したいとき

from bisect import bisect_left

List = [7,3,3,5,5,5,1]
ms = MultiSet(List)

for v in List:
    # vを1個追加
    ms.add(v,1)

print(bisect_left(ms,5)) # 4 (1-indexed)

簡単!

検証

ABC241 D問題

1165ms (2s制限)

Q = int(input())
List = []
QueryList = []
for _ in range(Q):
    QueryList += list(map(int,input().split())),
    if QueryList[-1][0] == 1:
      List += QueryList[-1][1],
 
from bisect import bisect_right, bisect_left
 
# 番兵
ms = MultiSet(List + [float('inf')])
ms.add(float('inf'),1)
 
for i,*q in QueryList:
  if i==1:
    x, = q
    ms.add(x,1)
  elif i==2:
    if len(ms)==1:print(-1);continue
    x,k = q
    idx = bisect_right(ms,x) - 1
    print([-1,ms[idx-k+1]][0 < idx-k+1 < len(ms)])
  else:
    if len(ms)==1:print(-1);continue
    x,k = q
    idx = bisect_left(ms,x)
    idx += idx==0
    print([-1,ms[idx+k-1]][0 < idx+k-1 < len(ms)])

0 < hogehoge < len(ms)は1-indexedなのと番兵除外のため

参考記事

BITの実装

BITでの二分探索

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1