【题意】

给你一些二维的点,判断多少个集合中存在一个好的点对,好的点对的定义为对于集合中任何一个点w(w≠u,w≠v),对于集合中的u,v都满足g(u,v,w)≥f(u,v)+f(u,w)+f(v,w)2。我们将式子化简一下为

f(u,v)>=f(u,w)+f(v,w)
我们知道两点之间线段最短,所以这个式子就是
f(u,v)=f(u,w)+f(v,w)
满足这个条件的集合个数,可以明显的看出这个集合中的所有的点一定是共线的,如果共线的点数为n,那么集合数目就是
ans=C(n2)+C(n3)+C(n4)+⋯+C(nn)=2n−1−n
如何处理出共线的点的数目?

【解题方法】

用map来记录对应斜率的线段的点的个数,然后枚举点,对每个点用map存下和这个店斜率相等的所有点,还有不要忘记最后处理这个点单独为重点的情况。

【AC code】


#include <map>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
#define ll long long
const int maxn=1010;
const int mod=1e9+7;
ll gcd(ll a,ll b){
    return b==0?a:gcd(b,a%b);
}
struct node{
    ll x,y;
    node(){}
    node(ll x,ll y):x(x),y(y){}
    bool operator <(const node &a)const{
        return x==a.x?y<a.y:x<a.x;
    }
    bool operator ==(const node &a)const{
        return (x==a.x&&y==a.y);
    }
    node operator -(const node &a)const{
        return node(x-a.x,y-a.y);
    }
    node operator *(const ll &a)const{
        return node(x*a,y*a);
    }
    node operator /(const ll &a)const{
        return node(x/a,y/a);
    }
}p[maxn];
int cnt[maxn];//统计重点数量
ll mi[maxn];//预处理二分幂
map<node,ll>mp;//记录每一个对应斜率对应的点数
ll ans;//记录答案
void init(){
    memset(cnt,0,sizeof(cnt));
    mi[0]=1;
    for(int i=1; i<=1000; i++) mi[i]=(mi[i-1]*2)%mod;
}
int main()
{
    init();
    int T,n;
    scanf("%d",&T);
    while(T--){
        scanf("%d",&n);
        memset(cnt,0,sizeof(cnt));
        cnt[0]=1;
        for(int i=0; i<n; i++) scanf("%I64d%I64d",&p[i].x,&p[i].y);
        int m=0;
        sort(p,p+n);
        for(int i=1; i<n; i++){
            if(p[m]==p[i]) cnt[m]++;
            else{
                p[++m]=p[i];
                cnt[m]++;
            }
        }
        node now;
        ans=0;
        if(m==0){
            ans=mi[cnt[0]]-1-cnt[0];
            printf("%I64d\n",ans);
            continue;
        }else{
            for(int i=0; i<=m; i++){
                mp.clear();
                for(int j=i+1; j<=m; j++){
                    now=p[i]-p[j];
                    //统一向量
                    if(now.x<0) now=now*(-1);
                    //向量化简
                    ll g=gcd(abs(now.x),abs(now.y));
                    now=now/g;
                    mp[now]+=cnt[j];
                }
                //在起点中选出至少一个数和共线的至少选出一个数组合
                for(auto it=mp.begin(); it!=mp.end(); it++){
                    ans=(ans+(((mi[cnt[i]]-1)*(mi[it->second]-1))%mod)%mod)%mod;
                }
                //处理重点
                ans=(ans+(mi[cnt[i]]-1-cnt[i]))%mod;
            }
            printf("%I64d\n",ans);
        }
    }
}