目次
トヨタシステムズプログラミングコンテスト2025(AtCoder Beginner Contest 431)F,G 問題メモ
F - Almost Sorted 2
問題文
- 長さ $N$ の整数列 $A=(A_1,A_2,\ldots,A_N)$ および正整数 $D$ が与えられます。
- $A$ を並び替えることで得られる整数列 $B=(B_1, B_2, \ldots, B_N)$ であって、次の条件を満たすものの個数を $998244353$ で割ったあまりを求めてください。
- すべての $i\ (1\leq i\leq N-1)$ に対して $B_{i+1}\geq B_i-D$ が成り立つ。
制約
- $2\leq N\leq 2\times 10^5$
- $1\leq D\leq 10^6$
- $1\leq A_i\leq 10^6$
- 入力は全て整数
解法
挿入DP。
$B$ を空配列で初期化して、値の小さい順に挿入して構成していくことを考える。
例えば $9$ を挿入しようとしている時、$D=3$ なら、「末尾もしくは $6,7,8$ の前」にのみ、挿入できる。
大きくなる分にはいくら大きくなってもいいので、条件を満たしている配列にこのように挿入して、条件が崩れることは無い。
D=3 ↓ ↓ ↓ ↓ 9の挿入可能位置 暫定B [ 2 1 6 5 2 8 7 ]
$A_i$ は、暫定 $B$ に含まれる「$A_i-D$ 以上 $A_i$ 未満の値の個数 $+1$(末尾の分)」の挿入候補がある。
この数は、暫定 $B$ の並びに依らず全ての暫定配列で同じ個数だけある。
ただし同じ値が含まれる場合に注意が必要。数列として同じものは1つとして数える。
したがって、同じ値はまとめて挿入する。
$n$ 箇所の候補に $r$ 個の値を挿入するのは、${}_n \mathrm{H}_r$ で求められる。
G - One Time Swap 2
問題文
- 長さ $N$ の整数列 $A=(A_1,A_2,\ldots,A_N)$ が与えられます。
- 整数の組 $(l, r) \ (1\leq l\lt r\leq N)$ に対し、$f(l,r)$ を「$A$ の $l$ 番目の要素と $r$ 番目の要素を入れ替えることで得られる整数列」とします。
- 長さ $\frac{N(N-1)}{2}$ の整数列の列 $B=(B_1,B_2,\ldots,B_{\frac{N(N-1)}{2}})$ を次の手順で生成します。
- 空の列 $B=()$ を用意する。
- 各整数の組 $(l, r)\ (1\leq l\lt r\leq N)$ に対し、$B$ に $f(l,r)$ を追加する。
- $B$ を数列の辞書順でソートする。
- $Q$ 個のクエリが与えられるので、順に処理してください。$i$ 番目のクエリは以下の通りです。
- 整数 $k$ が与えられる。
- $B_k=f(l,r)$ となるような整数の組 $(l, r) \ (1\leq l\lt r\leq N)$ を一つ求めそれを出力せよ。
制約
- $2\leq N\leq 2\times 10^5$
- $1\leq Q\leq 2\times 10^5$
- $1\leq A_i\leq N$
- $1\leq k\leq \frac{N(N-1)}{2}$
- 入力は全て整数
解法
なかなか必要な手順が多い。
だが、1つ1つは $C~E$ 問題でも出そうなアルゴリズムの積み重ねなので、
「こういうものを求めたい」→「このアルゴリズムを使えば求められる」という小問題を1つ1つ解決していけば、突飛な発想や難しい知識は要らない。
まず「元の数列より辞書順で小さくなるのか、変わらないのか、大きくなるのか」で大きく場合分けする。
- 小さくなる: $A_l \gt A_r$ なる $(l,r)$ → 転倒数だけ存在
- 変わらない: $A_l = A_r$ なる $(l,r)$ → 同じ値のペア数だけ存在
- 大きくなる: $A_l \lt A_r$ なる $(l,r)$
これで、どこに分類されるか大まかに分かる。
- 変わらない場合は、適当な同じ値のペアが答え。
- 大きくなる場合、反転させて小さくなる場合に帰着できる。
- $k←\dfrac{N(N-1)}{2}-k$
- $A←(N+1-A_1,N+1-A_2,...,N+1-A_N)$
小さくなる場合の中での順序を考える。$(l,r)$ が異なる2つが同じ配列になることは無いので、全ての大小関係が決まる。
- 小さくなる値は早く来るほどよい。→ $l$ が小さい
- $l$ が同じなら、交換で $l$ に来る値が小さいほどよい。→ $A_r$ が小さい
- $l,A_r$ が同じなら、大きくなる値は遅いほどよい。→ $r$ が大きい
この比較順序で決めることができる。 とはいえ、$(l,r)$ の候補は $O(N^2)$ あるので、$(l,A_r,r)$ を全て列挙してソート……なんてことはできない。
とりあえず、$l$ ごとにクエリを分けて、以下の情報を $l$ の位置にメモっておく。
- クエリ $i$ は、$l$ を左端とする中で $x$ 番目に小さい配列である
ここで、クエリ毎に適当な $l$ および $x$ は、 転倒数を求める過程で $L_i:=$「$i$ より右にあり、$A_i$ より小さい値の個数」を求めて累積和を取っておくと、 二分探索で求められる。$L_i$ は、つまり「$l=i$ として辞書順が小さくなる $(l,r)$ の個数」である。
その後、末尾から $(A_r,-r)$ をSortedSetなどに追加していく。
$l$ に到達したときに SortedSet 内にある $x$ 番目の $r$ が、対応する答えとなる。

