読者です 読者をやめる 読者になる 読者になる

競技プログラミングをするんだよ

ICPC国内予選突破を目標に一日一問題以上解いていきます。

UVa 109 - SCUD Busters

今までで一番読みづらい英文でした。
幾何ライブラリを作るべきだと思った問題です。

- 問題概要

王国同士が戦争をしています。
一つの王国は発電所一つと複数の民家で構成され、王国の領地は最も外側の民家もしくは発電所で囲まれています。すなわち、発電所及び民家で構成される点群で作られる最大の凸多角形です。
戦争が激化したため、SCUDミサイルを打ち、他国の発電所を破壊しはじめました。
ミサイルが王国の領地内に落ちると爆風が広がり、発電所のみを壊滅させます。
発電所が壊滅すると王国は成り立たなくなります。
各王国の発電所、民家の座標と発射された各ミサイルの着地座標が与えられるのでミサイルによって崩壊させられた王国の領地の総面積を出力してくださいという問題です。

- 解法

1.
一つの王国を構成する民家の数をn、王国の数をm、ミサイルの個数をkとします。
一つの王国の領地を構成する点群を求めます。
これは凸包を求めればよいのでO(mn\log n)
2.
次に、ミサイルが王国の領地内に落ちたかどうかの判定を行います。
ミサイルの着地座標が与えられたとき、全ての王国に対して領地内かどうかの判定を行います。
凸包を事前に求めていれば凸多角形の内外判定のアルゴリズムを使えます。
このアルゴリズムは凸包内のある点pを基準に、pから見て凸多角形のどの2頂点間にあるかを二分探索で求め、その二頂点間を結ぶ直線の右にあるか左にあるかで内外判定を行います。
一つの凸多角形に対して内外判定を行うときの計算量はO(\log n)
このアルゴリズムを全ての王国に適用するためO(km\log n)
内外判定を適用する王国を限定して枝刈りし、高速化は可能です。
内外判定のアルゴリズム自体はここでは解説しません。
3.
一度以上ミサイルの落ちた王国の面積を求めます。
凸包を事前に求めていればO(n)で求められます。
凸多角形を構成する頂点をp_1...p_nとすると、
S=\sum_{i=3}^n \frac{1}{2}|(p_{i-1}-p_1) \times (p_{i}-p_{i-1})|
で求められます。符号付面積の公式を使っています。
これを使って2でミサイルが領地内に落ちた判定をされた王国の総面積を求めればOKです。

最後に小数点第二位まで出力するのを忘れないようにしましょう。

- ソースコード

#include<iostream>
#include<fstream>
#include<sstream>
#include<string>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<ctime>
#include<stack>
#include<queue>
#include<set>
#include<map>
#include<vector>
#include<list>
#include<algorithm>
#include<utility>
#include<complex>

using namespace std;

#define reE(i,a,b) for(auto (i)=(a);(i)<=(b);(i)++)
#define rE(i,b) reE(i,0,b)
#define reT(i,a,b) for(auto (i)=(a);(i)<(b);(i)++)
#define rT(i,b) reT(i,0,b)
#define rep(i,a,b) reE(i,a,b);
#define rev(i,a,b) for(auto (i)=(b)-1;(i)>=(a);(i)--)
#define itr(i,b) for(auto (i)=(b).begin();(i)!=(b).end();++(i))
#define LL long long
#define all(b) (b).begin(),(b).end()

#define input_init stringstream ss; string strtoken, token; istringstream is
#define input_line  getline(cin, strtoken);is.str(strtoken);is.clear(istringstream::goodbit)
#define input_token(num) ss.str(""); ss.clear(stringstream::goodbit); getline(is, token, ','); ss << token; ss >> num

typedef complex<double> P;
typedef vector<P> Poly;

const LL INF = 1 << 30;
const double eps = 1e-8;

inline double dot(const P a, const  P b){//A dot B
	return a.real()*b.real() + a.imag()*b.imag();
}

inline double cross(const P a, const P b){//A cross B
	return a.real()*b.imag() - a.imag()*b.real();
}


bool less_P(const P& l, const P& r){
	if (l.real() == r.real())
		return l.imag() < r.imag();
	else return l.real() < r.real();
}

inline void convex_hull(Poly& p, Poly& res){
	int k = 0, t;
	res.clear();
	res.resize(2 * p.size());
	sort(all(p), less_P);
	reT(i, 0, (int)p.size()){
		while (k > 1 && (cross(res[k - 1] - res[k - 2], p[i] - res[k - 1])<eps))k--;
		res[k++] = p[i];
	}
	t = k;
	rev(i, 0, (int)p.size() - 1){
		while (k > t && (cross(res[k - 1] - res[k - 2], p[i] - res[k - 1])<eps))k--;
		res[k++] = p[i];
	}
	res.resize(k - 1);
}

inline bool convex_in(const Poly& l, const P& p){
	int a = 0, b = (int)l.size(), c;
	double A, C;
	const P g = (l[a] + l[b - 1] + l[b / 2]) / 3.0;
	while (b - a > 1){
		c = (a + b) / 2;
		A = cross(l[a] - g, p - l[a]);
		C = cross(l[c] - g, p - l[c]);
		if (cross(l[a] - g, l[c] - g) >= 0){
			if (A>-eps&&C < -eps)b = c;
			else a = c;
		}
		else {

			if (C < -eps || A > -eps)b = c;
			else a = c;
		}
	}
	return(cross(l[b%l.size()] - l[a], p - l[b%l.size()]) > -eps);
}

inline double convex_area(const Poly& l){
	double res = 0;
	reT(i, 2, (int)l.size()){
		res += (abs(cross(l[i-1] - l[0], l[i] - l[i-1])))/2;
	}
	return res;
}

vector<Poly> kingdoms;

int main(){
	int n;
	double x, y;
	while (cin >> n, n >= 0){
		Poly k = Poly(n);
		rT(i, n){
			cin >> x >> y;
			k[i].real(x); k[i].imag(y);
		}
		Poly king;
		convex_hull(k, king);
		kingdoms.push_back(king);
	}
	vector<bool> c(kingdoms.size());
	itr(i, c)(*i) = false;
	while (cin >> x >> y,(x!=0||y!=0)){
		P p(x, y);
		rT(i, (int)kingdoms.size())if (convex_in(kingdoms[i], p))c[i] = true;
	}
	double res = 0;
	rT(i, (int)kingdoms.size())if (c[i])res += convex_area(kingdoms[i]);
	/*rT(i, (int)kingdoms.size()){
		itr(j, kingdoms[i])printf("(%.0f,%.0f)", j->real(), j->imag());
		cout <<convex_area(kingdoms[i])<< endl;
	}*/
        printf("%.2f\n",res);
	return(0);

}