題意
平面上給2000個點,問有多少個銳角三角形。
解法
官方題解:
數一數銳角的數量A和直角+鈍角的數量B,那麼答案就是(A-2B)/3。 暴力算的話是O(n^3)的。使用極角排序+two pointers就可以做到O(n^2logn)這邊鈍角指代範圍在90度到180度之間的角(不包括90和180)。
對於每個點,以他爲中心進行一次極角排序。然後尺取找到鈍角和銳角數目。
自己寫了一下試試,發現尺取的地方比較噁心。
代碼
#include<bits/stdc++.h>
using namespace std;
const int SIZE = 2005;
typedef long long ll;
struct point2
{
ll x, y;
point2(ll xx = 0, ll yy = 0)
{
x = xx; y = yy;
}
point2 operator + (const point2 &a)const
{
return point2(x + a.x, y + a.y);
}
point2 operator - (const point2 &a)const
{
return point2(x - a.x, y - a.y);
}
ll operator * (const point2 &a)const
{
return x*a.x + y*a.y;
}
ll operator ^ (const point2 &a)const
{
return x*a.y - y*a.x;
}
bool operator < (const point2 &a)const
{
if (y * a.y <= 0)
{
if (y > 0 || a.y > 0)return y < a.y;
if (y == 0 && a.y == 0)return x < a.x;
}
return ((*this)^a) > 0;
}
} A[SIZE];
int main() {
int n;
vector<point2> v;
while(~scanf("%d",&n)){
for(int i = 0; i < n; i++) {
int a, b;
scanf("%d%d",&a,&b);
A[i] = point2(a, b); //這樣其實沒有寫成員函數賦值快我猜
}
ll rui = 0, dz = 0;
for(int ii = 0; ii < n; ii++) {
v.clear();
for(int j = 0; j < n; j++) {
if(ii == j) continue;
v.emplace_back(A[j] - A[ii]); //直接在尾部構造,比較快
}
sort(v.begin(), v.end());
v.insert(v.end(), v.begin(), v.end());
for(int i = 0, j = 0, k = 0, r = 0; i < n - 1; i++) {
while(j < i + n - 1 && !(v[i]^v[j]) && (v[i]*v[j]) > 0 ) j++;//0度角的結束位置
k = max(k, j);
while(k < i + n - 1 && (v[i]^v[k]) > 0 && (v[i]*v[k]) > 0) k++;//銳角的結束位置
r = max(r, k);
while(r < i + n - 1 && (v[i]^v[r]) > 0) r++;//鈍角的結束位置,對於大於180的角,當枚舉到另一條邊的時候會計算到這個情況
rui += k - j;
dz += r - k;
}
}
//cout << rui << " "<<dz<<endl;
printf("%I64d\n",(rui - 2*dz)/3);
}
}