TopCoder SRM 558 Div1 Meidum Ear
問題
座標平面の第一象限に青い点がいくつかあり、
x軸の正の部分に赤い点がいくつかある。
これらの点から青い点P, Qおよび、赤い点A, B, C, Dを選び、
- 三角形PADが三角形QBCを厳密に含み
- 角PAD, 角PDA, 角QBC, 角QCBが全て90度より厳密に小さい
とき、これら6点はEarをなすと言う。
Earをなす6点の選び方は何通りあるか、求めよ。
制約条件
赤い点の個数≦300
青い点の個数≦300
1≦それぞれの座標≦1000
方針
問題を長らく勘違いしていて、(APD, BQCも90度未満でないといけないと思っていた)
解けるけど実装が死ぬ問題だと思っていた…
O(n^2)でも解けるが、以下O(n^3)の解法
P, Qを固定して考える。
A, Bの選び方とC, Dの選び方は独立なので、
それぞれの選び方の場合の数を数えて掛け合わせればよい。
A, Bの選び方のほうだけを説明する。(C, Dのほうは左右が逆であとは同じ)
線分PAがQの上側を通るような、最も右のAをjとする。
選ぶBをiとする。
これに対して、Aの選び方は、max(j, i - 1) + 1通り。
iについてループをまわしてナイーブに足せばよい。
ソースコード
int n, m, rx[300], bx[300], by[300]; void parse(vector<string> &s, int *a, int &n){ n = 0; string t; rep(i, s.size()) t += s[i]; stringstream ss(t); while(ss >> a[n]) n++; } class Ear { public: long long getCount(vector <string> redX, vector <string> blueX, vector <string> blueY) { parse(redX, rx, n); parse(blueX, bx, m); parse(blueY, by, m); sort(rx, rx + n); ll ans = 0; rep(p, m) rep(q, m) if(by[p] > by[q]){ double x = bx[q] - 1.0 * by[q] * (bx[p] - bx[q]) / (by[p] - by[q]); int a = lower_bound(rx, rx + n, bx[q]) - rx - 1; int b = lower_bound(rx, rx + n, bx[q] + 1) - rx; int c = lower_bound(rx, rx + n, min(bx[p] * 1.0, x) - EPS) - rx - 1; int d = lower_bound(rx, rx + n, max(bx[p] * 1.0, x) + EPS) - rx; if(a < 0 || b >= n || c < 0 || d >= n) continue; int l = 0, r = 0; for(int i = a; i >= 0; i--) l += min(i - 1, c) + 1; for(int i = b; i < n; i++) r += n - max(i + 1, d); ans += (ll)r * l; } return ans; } };