geam1113’s diary

主にAtcoderのコンテストの備忘録を書きます。AtCoder緑と水色を行ったり来たりしています。

ABC231 参加記録

コンテスト中AC:A〜D

コンテスト後にFを自力ACしました。

D - Neighbors

問題文の条件は簡単ですが、判定法に抜けなどがあり、WAをたくさん出しました。

問題文の条件を満たすかどうかの判定は以下のようになります。

整数を頂点とし、A_iB_iに無向辺が存在するグラフとみなした時(連結とは限らないことにも注意)、

・次数が3以上の頂点があればNo

・閉路があればNo(最初これが抜けてWA量産しました)

・以上2つを満たさなければYes

 

次数3以上かは各頂点の辺の数を調べれば分かります。

 

閉路判定は各連結成分に対して、DFSを行い、訪問済みの頂点にぶつかるか調べれば良いです。

(別の問題で、DFSして頂点数Vと辺数Eを調べ、V-1=Eが成立するか調べる方法もありました)

 

特殊な形をした森であるか判定する問題と言い換えても良いかもしれません。

提出コード

 

F - Jealous Two

高橋くんがi番目、青木くんがj番目のプレゼントをもらった場合、満たすべき条件は、

A_i\leq A_jかつB_i\geq B_j
です。

A_iを基準として昇順ソートしておくと、j\lt iならA_j\leq A_iが成立します。

この時、B_iにのみ着目でき、既に出現したものの中でB_j\geq B_iであるjをカウントする問題となり、これは一見Binary Indexed Tree(BIT)で反転数を求める手法で求められそうです(B_iを配列としてもつために前処理として座標圧縮は必要なので、以下座標圧縮されているとします。)。

(反転数を求める方法を使うABCの過去問題の記事。ネタバレ注意)

 

しかし、実はA_jA_iが等しい場合、iを高橋くんに、jを青木くんにプレゼントするという順序を入れかえることができてしまい、単純に出現順で計算しても誤った答えになってしまいます。

 

そこで、自分の解法と公式解説の3パターンを載せておきます。

 

自分の解法

A_iを第一キー、B_iを第二キーとして昇順ソートします。

A_j\lt A_iを満たすjについては、BITで反転数を求める方法で求めます。

A_j=A_iの時は、iとjの大小関係は無いものとして、
A_i=A_jであるjの区間[l,r)を二分探索によって求める

・[l,r)内で、B_i以上となるB_jの個数を二分探索によって求める

ことで、計算しました。

実装上、気をつける点として、反転数を求める場合はB_iの出現数を+1する処理がありますが、これはA_iが同一である区間が終わってからまとめて行います。

提出コード

公式解法

A_iを第一キーとして昇順に、B_iを第二キーとして降順にソートします。

こうすると、A_j=A_iであっても、B_j\gt B_iなら、通常の反転数を求めように計算できます。

B_i=B_jの時の対処は、事前にランレングス圧縮しておけばよいです。

 

ランレングス圧縮すると、j\lt iならA_j\lt A_i,\ B_j \gt B_iが成立し、通常の反転数を求める方法が適用できます。

反転数を求める操作で得られたjの個数に対して、組(A_i,B_i)の要素の個数を掛ければよいです。

ユーザ解説(Wavelet Matrix)

このユーザ解説によれば、ウェーブレット行列によっても計算できるようです。

過去に実装したもので解いてみました。

 

(A_i,B_i)を二次元座標上の点(x_i,y_i)とみなします。

するとこの問題は、各iについて、

x_{min}\leq x\leq x_i
かつ

y_i \leq y \lt \infty
を満たす長方形領域の座標上の点をカウントする問題となります。(但し、x_{min}A_iのうち最小のもの)

これはウェーブレット行列によって、実現できます。

A_iをキーに組(A_i,B_i)を昇順ソートしておくと、

・二分探索によって、A_j\leq A_iを満たすjの範囲[l,r)を求められる。

・[l,r)の範囲で、B_i \leq B_j \lt \inftyを満たすjの個数をウェーブレット行列のrangefreq関数により、求める。

ことができます。(\inftyにはN+1など、Bのどの要素よりも大きい値を設定する)

提出コード