ABC268_G : Random Student ID を(ほぼ)ソートだけで解く
問題概要など
ここでは、新入生 の学籍番号の期待値は、 が の接頭辞であるような文字列の数を , が の接頭辞であるような文字列の数を としたときに(ただし、 にのみ を含む)、 を計算することで求めることができることを前提とします。詳しくは公式解説をご覧ください。
解法
- 各文字列 について、末尾に
|
を付け足した文字列を とします。 - と を同じ配列に入れ、昇順ソートします。ソート後の の index を , の index を とします。
- 以前に現れた
末尾が | でない文字列の数 - 末尾が | である文字列の数
です。
また、 です。 - 各 は、ソート後の配列を前から順に見ていくことで で求めることができます。よって配列のソートと合わせて期待値を で求めることができ、これは十分高速です。
解説
簡単な例で見ていきましょう。
とします。このとき、各 について、末尾に |
を付け足したものの配列を とすると、
となります。よって、これらを合体させてソートした配列 は、
となります。
分かりやすくするため、この配列 T の要素を順に縦に並べます。
これを見ると、どの と の間にも が接頭辞であるような文字列のみが過不足なく存在している ということが成り立つと推測できます。例えば、 と の間には、 が接頭辞である と があり、 と の間には、 が接頭辞であるような文字列は存在していないため何もありません。
また、このことを逆手に取ると、 の接頭辞である文字列の数は、 であるような の数と等しい ということも分かります。例えば、 を挟むように存在している文字列は と の 2 つであるため、この 2 つが のうち の接頭辞である文字列であることが分かります。
では、何故このことが成り立つのでしょうか?
ここで重要になのが、|
という文字の ASCII コードは 124 である、ということです。z
の ASCII コードは 122 であるため、ソートしたときに如何なる英小文字よりも後ろに来る ということが分かります。
このことから、ソートしたときに より後かつ より前に来る文字列 は、「 文字目まで と一致しており、 である」という条件を満たしている必要があります。すなわち、 は、この条件を満たすような の接頭辞である ということが成立します。
以上の議論により、この方法で が の接頭辞であるような文字列の数 , が の接頭辞であるような文字列の数 を求めることができると分かりました。
余談ですが、この性質から、 を開き括弧、 を閉じ括弧に置き換えて作成した括弧列は正しい括弧列であり、対応する括弧は必ず と の関係になっています。
先程の例で試すと ((())())(())
となることからも分かるかと思います。(この性質から何かしらの問題が生えるかもしれませんね)
実装例 (PyPy3, 798ms)
n = int(input()) t = [] mod = 998244353 cnt = 0 a = [0] * n b = [0] * n idx = [0] * n for i in range(n): s = input() t.append([s, i]) t.append([s + "|", i]) t.sort(key = lambda x: x[0]) for i in range(len(t)): if t[i][0][-1] != "|": cnt += 1 idx[t[i][1]] = i a[t[i][1]] = cnt else: cnt -= 1 b[t[i][1]] = (i - idx[t[i][1]] - 1) // 2 for i in range(n): ans = (a[i] - b[i] + n) * pow(2, mod-2, mod) % mod print(ans)