UVa 11726 Crime Scene
問題
n個の図形が与えられる。
それぞれの図形は半径r[i], 中心が(x[i], y[i])の円か、
それぞれの頂点が(x[i_0], y[i_0])..., (x[i_k-1], y[i_k-1])k[i]角形どちらか。
この図形の凸包の長さを求めよ。
制約条件
n≦100
k[i]≦10
答えは小数点以下6桁まで出力せよ。
方針
円を多角形で近似して凸包を取る。
これだと全然精度が足りないので、円弧由来の部分は円弧に戻して長さを出す。
円と多角形の接点も、その部分だけ計算しなおす。
円と円の共通接線も、その部分だけ計算しなおす。
すると精度のいい答えが出る。
円弧は、「最初にその円に入った場所」を覚えておいて、円から出るときに見ればよい。
(最後の円弧だけは別個に計算して足す)
周で計算をし始めるのは、円から出る場所が楽。
で、円から出る場所がない場合は、
全部の点が一つの円だけの場合か、円が一つもない場合。
これはそれぞれ適当に計算すればよい。
ソースコード
inline P getp(){ double x, y; cin >> x >> y; return P(x, y); } int main(){ int CS; cin >> CS; rep(cs, CS){ const int SPLIT = 500; int n; vector<P> c; vector<double> r; map<P, int> cid; vector<P> ps; double ans = 0; cin >> n; rep(i, n){ char t; cin >> t; if(t == 'c'){ c.pb(getp()); double x; cin >> x; r.pb(x); rep(j, SPLIT){ P p(c.back()); double u = 2 * PI * j / SPLIT; p += x * P(cos(u), sin(u)); ps.pb(p); cid[p] = (int)r.size() - 1; } } else{ int k; cin >> k; rep(j, k) ps.pb(getp()); } } ps = convex_hull(ps); P in_p(INF, INF); int start = -1; rep(i, ps.size()) if(cid.count(ps[i])){ if(!cid.count(ps[(i + 1) % ps.size()]) || cid[ps[i]] != cid[ps[(i + 1) % ps.size()]]){ start = i; break; } } if(start < 0){ //1 一個の円だけからなる if(cid.count(ps[0])){ rep(i, ps.size()) assert(cid.count(ps[i]) && cid[ps[i]] == cid[ps[0]]); ans = 2 * PI * r[cid[ps[0]]]; goto END; } //2 全部多角形上 rep(i, ps.size()) assert(!cid.count(ps[i])); start = 0; } rep(ii, ps.size()){ int i = (start + ii) % ps.size(); P p = ps[i], q = ps[(i + 1) % ps.size()]; if(!cid.count(p) && !cid.count(q)){ //多角形だけの部分 ans += abs(p - q); } else if(cid.count(p) != cid.count(q)){ //多角形-円の部分 bool out = 1; if(cid.count(q)){ swap(p, q); out = 0; } int id = cid[p]; P pp = nearest_tanCP(c[id], r[id], p, q); ans += abs(pp - q); if(out){ if(in_p.real() == INF) continue; double a = arg(in_p - c[id]); double b = arg(pp - c[id]); if(a > b) b += 2 * PI; ans += r[id] * (b - a); } else in_p = pp; } else if(cid.count(p) && cid.count(q)){ //両方円 int pid = cid[p], qid = cid[q]; if(pid == qid) continue; //弧は後で計算 L l = nearest_tanCC(c[pid], r[pid], c[qid], r[qid], p, q); ans += abs(l[0] - l[1]); if(in_p.real() < INF){ double a = arg(in_p - c[pid]); double b = arg(l[0] - c[pid]); if(a > b) b += 2 * PI; ans += r[pid] * (b - a); } in_p = l[1]; } } if(in_p.real() < INF){ P p(ps[start]); P q(ps[(start + 1) % ps.size()]); assert(cid.count(p)); int id = cid[p]; if(cid.count(q)){ L l = nearest_tanCC(c[cid[p]], r[cid[p]], c[cid[q]], r[cid[q]], p, q); p = l[0]; } else{ p = nearest_tanCP(c[cid[p]], r[cid[p]], p, q); } double a = arg(in_p - c[id]); double b = arg(p - c[id]); if(a > b) b += 2 * PI; ans += r[id] * (b - a); } END: printf("Case #%d: %.6f\n", cs + 1, ans); } return 0; }