版权声明:欢迎随便转载。 https://blog.csdn.net/a1214034447/article/details/87938993
题目链接:https://vjudge.net/problem/CodeForces-552D
解题思路:
枚举每个边,以这条边为直线上面的点数可以事先预处理然后用二分斜率找到。
假设直线上有i个点,那么就可以有i*(i-1)/2的边数组合方案,那么这个直线就会被计算i*(i-1)次,但这条直线的贡献只有i*(i-1)(i-2)/6次。所以每次枚举一个边就加上这边做直线上面点的数量-2就可以了,也就是i-2,最后将总的再除以3就是所有的负贡献之和。
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long ll;
const int mx = 2e3 + 10;
int x[mx],y[mx];
struct node
{
int x,y;
bool operator < (node A)const
{
return y*A.x < A.y*x;
}
}s[mx][mx];
int main(){
int n;
scanf("%d",&n);
for(int i=0;i<n;i++) scanf("%d%d",x+i,y+i);
for(int i=0;i<n;i++){
int siz = 0;
for(int j=0;j<n;j++){
if(i==j) continue;
if(x[i]==x[j]) s[i][siz++] = {1,mx*mx};
else{
if((x[i]-x[j])*(y[i]-y[j])>=0)
s[i][siz++] = {abs(x[i]-x[j]),abs(y[i]-y[j])};
else s[i][siz++] = {abs(x[i]-x[j]),min(y[i],y[j])-max(y[i],y[j])};
}
}
}
for(int i=0;i<n;i++) sort(s[i],s[i]+n-1);
int ans = 0;
for(int i=0;i<n;i++){
for(int j=i+1;j<n;j++){
node now;
if(x[i]==x[j]) now = {1,mx*mx};
else{
if((x[i]-x[j])*(y[i]-y[j])>=0)
now = {abs(x[i]-x[j]),abs(y[i]-y[j])};
else now = {abs(x[i]-x[j]),min(y[i],y[j])-max(y[i],y[j])};
}
int l = lower_bound(s[i],s[i]+n-1,now) - s[i];
int r = upper_bound(s[i],s[i]+n-1,now) - s[i];
ans += (r-l-1);
}
}
printf("%lld\n",1ll*n*(n-1)*(n-2)/6 - ans/3);
return 0;
}