题目连接:http://acm.hdu.edu.cn/showproblem.php?pid=5738
【题意】给定n个平面点,对于一个点集合P,存在一对点u,v.任意w∈P,u,v间的距离≥u,v,w,间距离和的一半。求这样的集合数量。
【分析】由于三角形两边之和一定大于第三边,那么只有在三点共线的情况下等式成立,而不等式则恒不成立。于是转换成共线点数目的组合数问题。需要注意的是会出现重点,重点需要额外处理。对输入的点进行判别,仅保存不同的点,同时保存这个点的数量。枚举每个点对其他点进行连接(注意j从i+1开始,避免重复),求出直线方程中的a和b。由于是同一个点出发,a,b确定k,k相同那么c一定相同,根据a,b累计直线上点的数目。对枚举的每个点,连接结束后计算直线上点数目形成的方案数。需要注意的是枚举的点是所有直线的交点,这个点如果是重点的话,每条直线计算方案数时会重复计算,需要在开始就计算重点的方案数,每条直线的方案数要减去该重点方案数。同时,还要减去不使用枚举点的方案数,避免后面枚举时重复计算。
【代码】
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<map>
using namespace std;
#define LL long long
const int mod=1e9+7;
int c[1010][1010];
int sum[1010];
int NN[1000010];
int num[100100];
struct Node
{
int x,y;
}node[1010];
bool operator<(Node n1,Node n2)
{
if(n1.x!=n2.x)
return n1.x<n2.x;
return n1.y<n2.y;
}
int gcd(int a,int b){
if(a==0 || b==0)
return 1;
if(a%b==0)
return b;
return gcd(b,a%b);
}
void init()
{
sum[1]=0;
for(int i=2;i<=1000;++i)
sum[i]=(2*sum[i-1]+i-1)%mod;
}
int main()
{
init();
int t,n,x,y,a,b,c;
double slo;
scanf("%d",&t);
while(t--)
{
map<Node,int> mp;
memset(num,0,sizeof(num));
int cnt=1;
Node tmp;
scanf("%d",&n);
for(int i=0;i<n;i++){
scanf("%d %d",&x,&y);
tmp.x=x;
tmp.y=y;
if(mp[tmp]==0){
num[cnt]++;
node[cnt]=tmp;
mp[tmp]=cnt++;
}
else
num[mp[tmp]]++;
}
int ans=0;
for(int i=1;i<cnt;i++)
{
ans=(ans+sum[num[i]])%mod;
map<Node,int> slope;
for(int j=i+1;j<cnt;j++)
{
a=node[i].x-node[j].x;
b=node[i].y-node[j].y;
if(a<0){
a=-a;
b=-b;
}
if(a==0)
b=1;
if(b==0)
a=1;
c=gcd(abs(a),abs(b));
a/=c;
b/=c;
tmp.x=a;
tmp.y=b;
slope[tmp]+=num[j];
}
for(map<Node,int>::iterator it=slope.begin();it!=slope.end();++it)
ans=(((ans+sum[it->second+num[i]])%mod-sum[it->second])%mod-sum[num[i]])%mod;
}
ans=(ans+mod)%mod;
printf("%d\n",ans);
}
}