ABC248 参加記録
コンテスト中AC:A〜D
D - Range Count Query
ウェーブレット行列が使えます。
ウェーブレット行列には以下の関数があります。
計算量は、今回の場合の要素の最大値をとって、です。
各クエリは、とすることで答えられます。
よって、すべてのクエリをで計算できます。
ウェーブレット行列の構築にかかるので、全体でです。
提出コード
E - K-colinear Line
の時は無限に存在するので、"Infinity"
です。
そうでない時、2点を決めれば直線を一意に定められるので2点を全探索します。
悩んだのが、同じ直線を2回以上使わないようにする処理です。
色々調べたことを書いておきます。
まず、2点を通る直線の式はググりました。
ので管理
式変形すると、
なので、
であり、これを管理します。
↓のtwitterで言及されていましたが、一意性を保つために正規化する必要があります。
熨斗袋 on Twitter: "格子点を通る直線は ax + by = c の形式で管理するのが楽なんじゃないかなと思う。"
熨斗袋 on Twitter: "gcd で割ったあと、min {(a, b, c), (-a, -b, -c)} で正規化"
にする
以下の2つは同じ式です。
これを同一視するため、として、でを割ります。符号を揃える
以下の2つは同じ式です。
これを同一視するため、(表現が正しいかわかりませんが、)符号を揃えます。
先程のツイートで言うところの、min({a,b,c}, {-a,-b,-c})を採用するのと同じだと思います。
下記実装では、構造体を作ってみました。
提出コード
ので管理
となります。小数での管理は誤差が心配なので、分数で管理します。
分数でも気をつける点があります。
- にする。すなわち、でを割る
- 符号を揃える。(正,正)なら(負,負)に、(正,負)にする。(これもmin({p,q},{-p,-q})で良さそうです)
また、この実装では、のパターン、すなわち、y軸に平行な直線の場合は例外処理が必要です。
として、y軸に平行な直線の座標を別に管理しておけば良いです。
これも構造体を作ってみました。
あと、公式解説と他の方の実装についても書いておきます。
探索済みの点対を管理
公式解説の方法です。
点とします。
flag[i][j]を点iと点jが探索済みならtrue、そうでなければfalseとします。
初め、全てfalseにします。
ある同一直線上にある全ての点の集合を、とします。に含まれるすべての点対に対して、
flag[i][j] = true
とします。
こうすることで、全探索の際にflag[i][j]=falseである点対だけ直線を求めればよいことになります。
基準となる2点を管理
他の方の実装を参考にした時によく見られました。
直線は2点によって一意に定めることができるので、先程の集合を何らかの基準でソートして、小さい方の2点を記録すること等で管理可能です。
点が直線上にあるかの判定
直線上であるかどうかの判定は、直線の式に代入していましたが、外積でいけるようです。