目次

AtCoder Beginner Contest 360 F,G問題メモ

AtCoder Beginner Contest 360

F - InterSections

F - InterSections

問題文

制約

解法

こういう問題の時、$l,r$ の範囲制約($0$ 以上 $10^9$ 以下)って、ある種お飾りというか、 「自然にやれば勝手にこの範囲に収まりますよ。なんならこの範囲に収まるというヒントを与えていますよ」ということも多いように思うが、 この問題の場合はわりとちゃんと“制約”であり、制約外で、問題の答えより $f(l,r)$ が大きくなる場合もあったりする。
仕様通りに「制約内での最大値を達成する $(l,r)$ の組」を求めなければならない。

それはともかく、解法の方針自体はそこまで難しくはない。

$l$ を固定して、$r$ についての問題を考えると、

                   l
区間1  |--------------| ********************
区間2      |----------------| **************
区間3        |---|                              *** の位置に r が来ると、
区間4                   |*************|         その区間と交差できることを意味する
区間5          |---|
区間6              |---------------|

よって、この ** を縦に合計して、一番 ** が多く重なっている箇所(のうち、最も左のもの)が、 $l$ を固定した場合の最適な $r$ となる。

$l$ を少し右に動かすと、

と、1つの区間に対し、$L_i,R_i$ と重なるタイミングで3回の更新が発生する。
逆に言うとそれ以外では変わらないので、$l$ を左から徐々に右に動かせば、全体で $O(N)$ 回の更新で済む。

区間加算・区間(最大値, 最大値が同じ場合は最小index)取得の遅延セグメント木で実装できる。

そのままでは座標の範囲が大きいが、「変化が生じる座標」「$l,r$ として最適になり得る座標」は $L_i,L_i+1,R_i,R_i+1$ のいずれかに限られる。これらのみ座標圧縮してセグメント木を構築すれば、サイズは $O(N)$ で済む。

で、範囲制約を考える。

実は、$l$ として最適になり得る座標には「$0$」も含まれる。
特に、交差できる区間が1つも無いとき、「$(0,1)$」が答えとなる。

また、$R_i+1=10^9+1$ なる区間が存在し、これが(範囲制約を考慮しない場合の)最適な $r$ になる場合がある。
これは答えの候補から除かないといけない。

これらのコーナーケースをちゃんと考慮すると、ACとなる。

Python3

G - Suitable Edit for LIS

G - Suitable Edit for LIS

問題文

制約

解法

1箇所を変えるだけでは、LISは高々1増えるか、増えないかとなる。
素朴なLISを求めた上で、「1増やせますか」を判定する問題と考えてよい。

好きな箇所を好きな値にできるので、だいたいは1増やせる。

具体的には、LISに “空間的かつ数値的な隙間” がある場合に可能となる。

添字列 $I$ は、$A$ によっては複数考えられるものの、そのうち1つでも上記を満たすものがあれば1増やせる。
全ての $I$ が上記を満たさないと、不可能ということになる。

ただし、便宜的に $A_0=-\infty,A_{N+1}=\infty$ とし、これらはLISに必ず含まれるとする。(最終的な答えからはもちろん除く)

【空間的かつ数値的な隙間の例】( _ はLISに不採用の値であることを示す)

-∞  1  3  _  _  4  _  _  5  8  9  _  _  11  ∞
   ~~ ~~  ~~~~~~   ~~~~~~  ~~ ~~  ~~~~~~   ~~
   ① ①    ②       ②    ① ①    ③     ①

①は、LISとして採用される箇所が隣接していて、空間的な隙間がない例。
②は、値が隣接していて、数値的な隙間がない例となる。
③は、空間的かつ数値的な隙間がある。このいずれかを例えば 10 にすると、LISを1増やせる。

LISを求めるDPの過程で、「このような隙間のあるLISが1つでもあるか?」を判定したい。
LISを求めるアルゴリズムには、二分探索するものもあるが、今回はセグメント木で求めるものをベースとする。

LIS長のみを求めるシンプルなDPでは、以下のDPをセグメント木に載せ、 $A_i$ の小さい順に(同じならindexが大きい順に)埋めていく。

i     1  2  3  4  5   ...  i-2  i-1  i
A    11  5  2  7 12         11   5  10   ※Ai <= Aj となる j については、未処理なのでDP[j]=0
DP    0  1  1  2  0          0   3
     |-----------------------------|     ←この区間のDPの最大値+1が、DP[i]

今回求めたいものは、以下のように拡張する。

i       1  2  3  4  5   ...  i-2  i-1  i
A      11  5  2  7 12         11   5  10
DP(a)   0  1  1  2  0          0   3
      |-------------------------|         ←この区間のDP(a)の最大値と、
      |-----------------------------|     ←この区間のDP(a)の最大値が同じなら、空間的な隙間がある
      
2回のクエリで、空間的な隙間の有無を確認できる。
空間的な隙間があるとき、短い方のクエリの (c)+1 < Ai なら数値的な隙間もあると判定できる。

セグメント木では、(a)が長いもの > (b)がTrueのもの > (c)が小さいもの の優先順位でマージしていく。

これで最後までDPした後、全体に対するクエリ結果の(b)がTrueなら1増やせる。

Python3