Codeforces 394(#231 Div1) E. Lightbulb for Minister
問題
n個の点および凸なm角形が与えられる。
m角形の内部で、n個の点からの距離の二乗の和が最小になる場所における、
距離の二乗の和の最小値を求めよ。
制約条件
n≦10^5
m≦10^5
座標の絶対値は10^6以下
方針
n個の点からの距離の二乗の和をf(x, y)とする。これを最小化したい。
m角形の内部という条件がなかったら、単に二変数の二次関数の最小値なので、
grad f = 0となる点が答え。
すなわち、Σx[i] - nx = 0, Σy[i] - ny = 0なので、単に重心。
重心が多角形の内部に入っていなかったときが問題で、
f(x, y)の等高線を考えてみると、これはx^2の係数とy^2の係数が等しいので円になっている。
なので、f(x, y)を最小にするような点はm角形の辺上か頂点のどれかであるはずで、
辺上にあるときは、その辺の両端のうちどちらかは、頂点の中でf(x, y)を最小にする頂点。
なので、f(x, y)が最小になる頂点の周りだけで最小値を探してやればよい。
二次関数なので直接最小値が求まるけれど、3分探索とかをしてもギリギリ間に合う。
ソースコード
int n, m; double ax, bx, cx, ay, by, cy; double dist(P p){ double res = 0; res += ax * p.real() * p.real() - bx * p.real() + cx; res += ay * p.imag() * p.imag() - by * p.imag() + cy; return res; } double solve(P p, P q){ double l = 0, r = 1; rep(it, 100){ double a = (l * 2 + r) / 3, b = (l + r * 2) / 3; if(dist(p * a + q * (1 - a)) < dist(p * b + q * (1 - b))) r = b; else l = a; } return dist(p * l + q * (1 - l)); } int main(){ cin >> n; G p(n); P g; ax = ay = n; rep(i, n){ cin >> p[i].real() >> p[i].imag(); g += p[i] / (double)n; bx += p[i].real() * 2; by += p[i].imag() * 2; cx += p[i].real() * p[i].real(); cy += p[i].imag() * p[i].imag(); } cin >> m; G q(m); rep(i, m) cin >> q[i].real() >> q[i].imag(); if(contains(q, g) != OUT){ printf("%.9f\n", dist(g)); return 0; } double mn = 1e20; int mni; rep(i, m){ double d2 = dist(q[i]); if(d2 < mn){ mn = d2; mni = i; } } double ans = solve(q[(mni + m - 1) % m], q[mni]); ans = min(ans, solve(q[mni], q[(mni + 1) % m])); printf("%.9f\n", ans); return 0; }