比赛的时候用枚举直线没做出来,QAQ。按照官方题解敲了一遍。
xjb推导一下可以知道best set一定是一些共线的点, 于是问题变成问有多少个子集共线. 首先, 把所有点按照(x,y)双关键字排序, 然后枚举最左边的点i, 那么其他点j一定满足j>i. 把在这个点右边的点都做下极角排序(按照gcd(dx,dy)1(dx,dy)排序), 统计下共线的就好了. 需要注意下对重点的处理.
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<vector>
#include<cmath>
#include<string>
#include<algorithm>
#include<set>
#include<map>
#include<cstring>
#include<queue>
#include<stack>
#include<list>
//#pragma comment(linker, "/STACK:1024000000,1024000000")
using namespace std;
typedef long long ll;
const int maxn=1000+12;
const ll inf=1e9;
const ll mod=1e9+7;
ll pw[maxn];
struct point {
ll x,y;
ll s,t;
void gcd(){
if(s==0||t==0){
if(s==0)t=1;
else if(t==0)s=1;
}
else{
int tt=__gcd(s,t);
s/=tt;t/=tt;
}
}
bool operator<(point zz)const {
if(s==zz.s)return t<zz.t;
return s<zz.s;
}
}p[maxn],tp[maxn];
bool cmp1(const point& p1,const point &p2){
if(p1.y==p2.y)return p1.x<p2.x;
return p1.y<p2.y;
}
int main(){
pw[0]=1;
for(int i=1;i<maxn;i++){
pw[i]=(pw[i-1]*2)%mod;
}
int t;
scanf("%d",&t);
while(t--){
int n;
scanf("%d",&n);
for(int i=0;i<n;i++){
scanf("%I64d",&p[i].x);
scanf("%I64d",&p[i].y);
}
sort(p,p+n,cmp1);
ll ans=0;
for(int i=0;i<n-1;i++){
int siz=0,cntp=0;
point now=p[i];
for(int j=i+1;j<n;j++){
if(p[j].x==now.x&&p[j].y==now.y){cntp++;continue;}
tp[siz]=p[j];
tp[siz].s=p[j].x-p[i].x;
tp[siz].t=p[j].y-p[i].y;
tp[siz++].gcd();
}
sort(tp,tp+siz);
ll cntl=0;
for(int j=0;j<siz;){//i,j make sure a line
int k,num=cntp+1;
for(k=j+1;k<siz;k++){
if((tp[k].x-now.x)*(tp[j].y-now.y)!=(tp[k].y-now.y)*(tp[j].x-now.x) )
break;
num++;
}
j=k;
cntl++;
ans=(ans+pw[num]-1+mod)%mod;
}
ans=(ans-(cntl-1)*(pw[cntp]-1+mod)%mod+mod)%mod;
}
printf("%I64d\n",ans);
}
return 0;
}