主要在這個雙指針掃描法上,它的意思是說把左右邊界找到,然後在這個區間內的就是符合條件的,(據說比二分優越)
這個開2倍邊界掙扎了我好久.
代碼是一個鈍角/直角貢獻了兩個銳角的辦法,銳角-2*鈍/直 再/3就是數量
也可以用 銳角/3 - 鈍/直
複雜度(n^2)*log(n)
還有就是這個EPS,根據dls的直播課所說現在x和y上界是1e9,atan2(1e9,1)和atan2(1e9-1,1)的精度是在1/(1e9^2)也就是1e-18級別.
EPS別開的太小.
#include <iostream>
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef double DB;
const DB PI = acos(-1.0);
const DB EPS = 1e-13;
int n;
struct NODE
{
DB x,y;
} a[2003];
DB sign(DB x)
{
if(abs(x)<= EPS)
return 0;
return x<0?-1:1;
}
double node[4003];
int CNT(DB A)
{
int r = 1,res = 0;
for(int l=1; l<n; l++)
{
int ecnt = 0;
while( sign(node[l+ecnt]-node[l]) == 0)++ecnt;
l = l+ecnt-1;//最後一個共線的點
r = max(l+1,r);//開始第一個
while(r-l+1 < n && sign(node[r]-node[l]-A)<0)r++;
res += (r-l-1)*ecnt;
}
return res;
}
int main()
{
// freopen("in.txt","r",stdin);
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
while(cin>>n)
{
for(int i=1; i<=n; i++)
cin>>a[i].x>>a[i].y;
LL ans = 0,notans = 0;
for(int i=1; i<=n; i++)
{
int cnt = 0;
for(int j=1; j<=n; j++)
{
if(i == j)continue;
node[++cnt] = atan2(a[i].y-a[j].y,a[i].x-a[j].x);
}
sort(node+1,node+cnt+1);
for(int j=cnt+1; j<=2*cnt; j++)
node[j] = node[j-cnt]+2*PI;//[1,cnt]是在-PI到PI,判定的時候要求差值小於PI,所以+2*PI,變成了[PI,3PI],這樣邊界就接上了
int res = CNT(PI/2);
ans += res;
notans += CNT(PI)-res;
}
cout<<(ans-2*notans)/3<<endl;
}
}