1. 程式人生 > 實用技巧 >[CF1284E]New Year and Castle Construction

[CF1284E]New Year and Castle Construction

題面

http://codeforces.com/problemset/problem/1284/E

題解

經過持久的思考(看題解),發現直接正向去求一個點在多少個四邊形內部是行不通的,所以取補,統計一個點在多少個四邊形外部。

對於組成四邊形的四個點和一個點P,把四個點按照與P連線的極角升序排序。設排好序後順序是A,B,C,D,那麼不管這四個點以何種順序連線成四邊形,P都在該四邊形外部,當且僅當\(\angle APB\)\(\angle BPC\)\(\angle CPD\)\(\angle DPA\)中有一個大於\(\pi\)(注意:此處的\(\angle APB\)指的是逆時針方向上以PA為始邊,PB為終邊的\([0,2\pi)\)

之間的角,可以大於\(\pi\)

  • 如圖,P在四邊形內部,四個角都\(<\pi\)

  • 如圖,P在四邊形外部,\(\theta_2>\pi\)

  • 本圖似乎是反例,其實並不是,因為這四點如果按照A-C-D-B的順序連線,P就在四邊形內部了。

所以程式的流程就很清晰了:先列舉P,然後把除了P以外的所有點極角排序,列舉這個大於\(\pi\)的角的始邊,假設終邊有x個選擇(終邊可選擇的區域的邊界遞增,x可均攤O(1)維護),那麼此時的四邊形數就是\({\sum_{i=0}^{k}}{\tbinom{i}{2}}\),利用平方和公式求和即可。

總時間複雜度\(O(n^2 \log n)\)

程式碼

#include<bits/stdc++.h>

using namespace std;

#define ll long long
#define rg register
#define In inline

const ll N = 2500;

In ll read(){
	ll s = 0,ww = 1;
	char ch = getchar();
	while(ch < '0' || ch > '9'){if(ch == '-')ww = -1;ch = getchar();}
	while('0' <= ch && ch <= '9'){s = 10 * s + ch - '0';ch = getchar();}
	return s * ww;
}

struct vec{
	ll x,y;
	vec(){}
	vec(ll _x,ll _y){x = _x,y = _y;}
	In friend vec operator + (vec a,vec b){
		return vec(a.x + b.x,a.y + b.y);
	}
	In friend vec operator - (vec a,vec b){
		return vec(a.x - b.x,a.y - b.y);
	}
	In friend ll Dot(vec a,vec b){
		return a.x * b.x + a.y * b.y;
	}
	In friend ll Cross(vec a,vec b){
		return a.x * b.y - a.y * b.x;
	}
	In friend bool InUpper(vec a){
		return a.y > 0 || (a.y==0&&a.x>0);
	}
}p[N+5];

struct ang{ //任意角
	vec v;ll r; 
	ang(){}
	ang(vec _v,ll _r){v = _v,r = _r;}
	In friend ang Rot180(ang a){
		if(InUpper(a.v))a.r++;
		a.v.x = -a.v.x;
		a.v.y = -a.v.y;
		return a;
	}
	In friend bool operator < (ang a,ang b){
		if(a.r != b.r)return a.r < b.r;
		bool k1 = InUpper(a.v),k2 = InUpper(b.v);
		if(k1 != k2)return k1 < k2;
		return Cross(a.v,b.v) > 0;
	}
};

ll n;
ang a[2*N+5];

In ll C2(ll x){return x * (x - 1) / 2;}

In ll C4(ll x){return x * (x - 1) * (x - 2) * (x - 3) / 24;}

In ll sum1(ll x){return x * (x + 1) / 2;}

In ll sum2(ll x){return x * (x + 1) * ((x<<1)|1) / 6;}

In ll sum(ll x){
	return (sum2(x) - sum1(x)) >> 1;
}

ll calc(ll id){
	ll cnt = 0;
	for(rg int i = 1;i <= n;i++)if(i != id)a[++cnt] = ang(p[i] - p[id],0);
	sort(a + 1,a + cnt + 1);
	ll m = cnt;
	for(rg int i = 1;i <= m;i++)a[++cnt] = ang(a[i].v,1);
	a[++cnt] = ang(vec(-1,0),2); //防溢位
	int l = 0,r = 0;
	ll ans = 0;
	for(rg int i = 1;i <= m;i++){
		ang al = Rot180(a[i]);
		while(a[l] < al)l++;
		r = i + m - 1;
		ans += sum(r - l);
	}
	return ans;
}

int main(){
	n = read();
	for(rg int i = 1;i <= n;i++){
		ll x = read(),y = read();
		p[i] = vec(x,y);
	}
	ll all = n * C4(n - 1);
	ll ans = 0;
	for(rg int i = 1;i <= n;i++)ans += calc(i);
	ans = all - ans;
	cout << ans << endl;
	return 0;
}