题意:

给出平面上的n个点,每个点有唯一的标号(label),这n个标号的集合记作S,点可能重合。求满足下列条件的S的子集T

的数目:

1. |T|2

2.T中的点共线


这个题的重点落在了:

一:如何处理重复的点?

二:如何判断共线?


判断重复,算是比较简单:把所有的点排序,先按x升序,再按y升序,就方便判断点的重合了

如何判断共线呢?

采用斜率!斜率用(x,y)来表示,先化归成不可约

那么需要对相同的斜率的进行计算和排重


看了各位大神的题解之后,采用


map<pair<long long,long long>,int> vis;
map<pair<long long,long long>,int>::iterator it;
来查重

pair这个是斜率

后面的int呢,就是出现了多少次


所以我们可以枚举每一个点

然后看之后的点,有多少个与它相同,有多少组点与其共线,那么用乘法原理,就能够计算出来

因为,每组的点,要么取,要么不取


重合的点数记为res(不算自己)

某一个斜率下有的点记为cnt
方案一:只选用重合的点,那么至少选1个,不能不选。所以方案数目为pow(2,res)-1

方案二:可以选用重合的点,至少选择一个非重合的点

那么用乘法原理,pow(2,res)*【pow(2,cnt)-1】

剩下的就是对不同的点,不同的斜率进行枚举


注意,可以提前打表把2的幂次打表好,不然容易超时的


#include<bits/stdc++.h>
using namespace std;

map<pair<long long,long long>,int> vis;
map<pair<long long,long long>,int>::iterator it;
const int mod=1e9+7;
const int maxn=1050;
int n,t;
struct node{
	long long x,y;
}num[maxn];
long long pow2[maxn];

bool cmp(node a,node b){
	if (a.x==b.x) return a.y<b.y;
	return a.x<b.x;
}

/*
long long qpow(long long a,long long b){
	if (b<0) return 0;
	a%=mod;
	long long ans=1;
	while(b){
		if (b&1) ans=(ans*a)%mod;
		b>>=1;
		a=(a*a)%mod;
	}
	return ans;
}
*/

int main(){
	//freopen("input.txt","r",stdin);
	pow2[0]=1;
	for(int i=1;i<=1000;i++)
		pow2[i]=(pow2[i-1]*2)%mod;
	scanf("%d",&t);
	while(t--){
		scanf("%d",&n);
		for(int i=1;i<=n;i++)
			scanf("%I64d%I64d",&num[i].x,&num[i].y);
		sort(num+1,num+n+1,cmp);
		long long ans=0;
		for(int i=1;i<=n;i++){
			vis.clear();
			int res=0;
			for(int j=i+1;j<=n;j++){
				if (num[j].x==num[i].x&&num[j].y==num[i].y) res++;
				else{
					long long dx=num[i].x-num[j].x;
					long long dy=num[i].y-num[j].y;
					long long GCD=__gcd(dx,dy);
					if (GCD){
						dx/=GCD;dy/=GCD;
					}
					vis[make_pair(dx,dy)]++;
				}
			}
			if (res>1){
				//ans+=(qpow(2,res-1)-1)%mod;
				ans+=(pow2[res]-1+mod)%mod;
				ans%=mod;
			}
			for(it=vis.begin();it!=vis.end();it++){
				long long cnt=(it->second);
				ans=(ans+((pow2[cnt]-1)*pow2[res])%mod)%mod;
			}
		}
		printf("%I64d\n",(ans+mod)%mod);
	}
	return 0;
}