F - Rearrange Query Editorial
by
toam
\(\{A_l,A_{l+1},\ldots,A_r\}\) と \(\{B_L,B_{L+1},\ldots,B_R\}\) が多重集合として一致しているか?というクエリを処理する問題です.
\(2\) つの集合が一致しているかどうかを高速に確率的に判定する方法として Zobrist Hash が有名です.多重集合の一致判定も同様にハッシュを用いることで高速に解くことができます.
ハッシュ関数 \(H\) として,以下の要件が満たされていると良いです.
- 多重集合 \(S\) のハッシュ値 \(H(S)\) が高速に計算できる
- 二つの多重集合 \(S,T\) が一致しているならば \(H(S)=H(T)\)
- 二つの多重集合 \(S,T\) が一致していないならば,高確率で \(H(S)\neq H(T)\)
例えば,\(1\) 以上 \(N\) 以下の各整数に適当なハッシュ値を割り当てて,\(S\) のハッシュ関数を「\(S\) に含まれる各要素のハッシュ値の総和 \(\mathrm{mod}\ P\) (\(P\) は適当な大きい素数)」とすることで上を満たすことができます.
\(1\) つ目の要件について,\(\{A_l,A_{l+1},\ldots,A_r\}\) および \(\{B_L,B_{L+1},\ldots,B_R\}\) のハッシュ値は,あらかじめハッシュ値の累積和を計算しておくことで \(O(1)\) で求めることができます.\(2\) つ目の要件について,二つの多重集合 \(S,T\) が一致しているならば \(H(S)=H(T)\) となるのは容易にわかります.\(3\) つ目の要件を示すのは難しいですが,二つの多重集合 \(S,T\) が一致していないならば,高確率で \(H(S)\neq H(T)\) となっています(Schwartz–Zippel lemma を用いて証明ができるようです.参考 (noshi さんの記事)).
以上で述べたように,\(1\) 以上 \(N\) 以下の各整数にハッシュ値をランダムに設定し,累積和を前計算することで,各クエリに対して \(O(1)\) で高確率で正しい答えを求めることができます.全体の計算量は \(O(N+Q)\) です.
import random
mod = (1 << 61) - 1
N, Q = map(int, input().split())
A = list(map(lambda x: int(x) - 1, input().split()))
B = list(map(lambda x: int(x) - 1, input().split()))
hash = [random.randint(1, mod - 1) for i in range(N)]
cumA = [0] * (N + 1)
cumB = [0] * (N + 1)
for i in range(N):
cumA[i + 1] = (cumA[i] + hash[A[i]]) % mod
cumB[i + 1] = (cumB[i] + hash[B[i]]) % mod
for i in range(Q):
l, r, L, R = map(int, input().split())
if (cumA[r] - cumA[l - 1]) % mod == (cumB[R] - cumB[L - 1]) % mod:
print("Yes")
else:
print("No")
posted:
last update:
