[CF1284E]New Year and Castle Construction
阿新 • • 發佈:2020-10-04
題面
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)\)
- 如圖,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; }