Official

G - Guarding Plan Editorial by confeito


簡単のため、 \(x\) 座標が小さい方を「左」、大きい方を「右」、\(y\) 座標が小さい方を「下」、大きい方を「上」と表します。

最初から不要な警備員(必要な警備員以外の警備員)は除去して考えます。

除去して良い理由 不要な警備員 $x$ について、 $x$ を監視している警備員を $y$ とする。一連の操作で、 $y$ は消滅しないので、 $x$ が必要な警備員に変わることはない。また、「 $x$ と別の警備員を選んでその線分上に新たな警備員を配置する」という操作も考えなくて良い。なぜなら、選んだ別の警備員を $z$ 、新たに配置した警備員を $w$ とすると、 $y$ と $z$ を選んで適切に点を選べば、 $w$ の監視領域を完全に包含するような警備員を配置できるからである( $z=y$ の場合は、 $w$ が $y$ に監視されているのでこの操作自体無駄であり行われることはない)。よって、一連の操作において $x$ の存在が影響することはないので、除去して考えて良い。


この時、操作は上側凸包上で隣り合う警備員を選んで行うもののみを考えれば良いです。

理由 新たに配置された警備員 $x$ が上側凸包上にいない場合、ある上側凸包上の点が存在し、そこに警備員を配置すれば、 $x$ の監視領域を完全に包含するような領域を監視できる。よって、上側凸包上以外に警備員を配置する行為は考えなくて良い。上側凸包上に警備員を配置するには、凸包上で隣り合う警備員を選べば良い。


これにより、この問題は上側凸包上にいる警備員を境界として分割することができ、適切な平行移動によって、警備員が点 \(A(0,a),B(b,0)\) および直角三角形 \(AOB\)\(O\) は原点)の内部(境界除く)のみにいる場合に帰着できます。以下はこの問題設定で考えます。

直角三角形の内部にいる警備員は、最終的に、線分 \(AB\) 上に新たに配置された警備員に監視されることで不要な警備員になるか、最後まで必要な警備員のままでいるかのいずれかです。そこで、問題を次のように言い換えます。

  • 以下の \(2\) つの操作により、 \(AOB\) の内部の全ての警備員を取り除く方法を考える。操作 \(1\) と操作 \(2\) の操作回数の合計を最小化した上で操作 \(1\) の回数を最小化せよ。
  1. 線分 \(AB\) 上の点を選ぶ。その点に警備員を置いた時に監視される警備員を全て取り除く。
  2. 警備員を \(1\) 人選び、取り除く。

ここで、直角三角形の内部にいる警備員 \(x\) に注目します。操作 \(1\) によって \(x\) を消せるような点の集合は、線分 \(AB\) 上の閉区間となります。この区間を、 \(x\)削除区間と呼ぶことにします。最初から不要な警備員は既に取り除いているので、 \(x\) の削除区間に別の警備員の削除区間が包含される、というような関係はありません。よって、各警備員の削除区間を始点でソートすると、終点もソートされた状態になります。

これを踏まえると、問題は以下のような \(1\) 次元の問題に言い換えられます。

  • 区間が \(n\) 個あり、 \(i\) 番目の区間は \([a_i,b_i]\) である。なお、始点と終点はそれぞれソートされていて、 \(a_1<a_2<\dots<a_n\) 及び \(b_1<b_2<\dots<b_n\) が成立する。次の操作を繰り返すことで全ての区間を消す方法を考える。操作 \(1\) と操作 \(2\) の操作回数の合計を最小化した上で操作 \(1\) の回数を最小化せよ。
    1. ある点を選び、その点を含む区間を全て消す。
    2. ある区間を選び、その区間を消す。

この問題について、まず \([a_1,b_1]\) を消すことを考えます。すると、この区間を操作 \(1\) で消す場合、実は \(b_1\) を選んで消すことだけを考えれば良いです。なぜなら、 \(a_1\leq c<b_1\) なる点 \(c\) を選ぶよりも \(b_1\) を選んだ方がさらに多くの区間を消せるからです。よって、残った区間を小さい方から見て消していく場合、「区間の終点を選んで操作 \(1\) を行う」「操作 \(2\) を行う」の \(2\) 通りの操作のみを考えれば良いです。

よって、以下のようなDPで解くことができます。

  • \(dp[i]:=\) 上で挙げた \(2\) 通りの操作で \(i\) 番目以前の区間を全て消す方法のうち、操作 \(1,2\) の合計回数の最小、およびその時の操作 \(1\) の回数の最小

遷移の際に、操作 \(1\) でどこまで消せるかを求める必要がありますが、これは二分探索で可能です。区間の終点の \(x\) 座標と \(y\) 座標はそのまま持つと有理数になりますが、今回必要なのは「区間の終点を選んだ時に、特定の区間まで消せるか」を判定することだけであり、これは有理数構造体などを使わなくても実装できます。なお、浮動小数点数を使った解答はなるべく落とすようにテストケースを作成しています。

計算量は \(O(N\log N)\) になります。

posted:
last update: