コード
"""Mo's Algorithm
・使い方
mo=Mo(A,queries): インスタンス生成
ans=mo.ans: 各クエリに対する答えが順番に入ってるリスト
MoStatusの中のTODOのところは、問題ごとに変える
参考URL(ありがとうございます!)
ei3333さんの記事【https://ei1333.hateblo.jp/entry/2017/09/11/211011】
Nyaanさんの記事【https://nyaannyaan.github.io/library/misc/mo.hpp.html】
"""
import math
from operator import itemgetter
class MoStatus():
def __init__(self, max_element):
self.cnt = [0] * (max_element + 1)
self.val = 0
def add(self, element):
self.cnt[element] += 1
# TODO
def discard(self, element):
self.cnt[element] -= 1
# TODO
class Mo():
def __init__(self, lis, init_queries):
self.N = len(lis)
self.lis = lis
self.Q = len(init_queries)
self.max_r = -1
self.init_queries = []
for qi, query in enumerate(init_queries):
l, r = query
self.init_queries.append((l, r, qi))
if self.max_r < r:
self.max_r = r
self.status = MoStatus(max_element=max(self.lis))
self.section_width = None
self.separate_cnt = None
self.separated_queries = None
self.separated_queries_generator()
self.ans = [0] * self.Q
self.solve()
def separated_queries_generator(self):
self.section_width = int(
math.sqrt(3) * self.max_r / math.sqrt(2 * self.Q)) + 1
self.separate_cnt = (
self.max_r + self.section_width - 1) // self.section_width
self.separated_queries = [[] for _ in range(self.separate_cnt + 1)]
for query in self.init_queries:
l, r, qi = query
idx = l // self.section_width
self.separated_queries[idx].append(query)
for i in range(self.separate_cnt):
self.separated_queries[i].sort(key=itemgetter(1), reverse=i % 2)
def solve(self):
prev_l, prev_r = 0, -1
for queries_list in self.separated_queries:
for query in queries_list:
nl, nr, qi = query
if nl < prev_l:
for i in range(nl, prev_l):
element = self.lis[i]
self.status.add(element)
else:
for i in range(prev_l, nl):
element = self.lis[i]
self.status.discard(element)
if prev_r < nr:
for i in range(nr, prev_r, -1):
element = self.lis[i]
self.status.add(element)
else:
for i in range(prev_r, nr, -1):
element = self.lis[i]
self.status.discard(element)
prev_l, prev_r = nl, nr
self.ans[qi] = self.status.val
つかいかた
コードのはじめに書いてあるコメントの通りです。
queriesには、0-indexedの閉区間(l,r)を入れてね(半開区間じゃないから注意してね)。
Mo's Algorithm特有の区間の分割だったり動かし方だったりはMoクラスの中で書いてて、基本的にはMoStatusクラスの中身を問題ごとに書き換えればいいようにしてます。
ACコード
Mo's Algorithmをつかって解ける問題2つでACできてます。
もしバグがあったらごめんなさい。(コメントなどで教えてくれるとうれしいです!)
ABC293G Triple Index → https://atcoder.jp/contests/abc293/submissions/40343024
ABC242G Range Pairing Query → https://atcoder.jp/contests/abc242/submissions/40342743
参考URL
参考にさせていただいた記事2つです。ありがとうございます!
ei3333さんの記事 → https://ei1333.hateblo.jp/entry/2017/09/11/211011
Nyaanさんの記事 → https://nyaannyaan.github.io/library/misc/mo.hpp.html