Eureka
Time Limit: 8000/4000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 4007 Accepted Submission(s): 1096
Problem Description
Professor Zhang draws n points on the plane, which are conveniently labeled by 1,2,…,n. The i-th point is at (xi,yi). Professor Zhang wants to know the number of best sets. As the value could be very large, print it modulo 109+7.
A set P (P contains the label of the points) is called best set if and only if there are at least one best pair in P. Two numbers u and v (u,v∈P,u≠v) are called best pair, if for every w∈P, f(u,v)≥g(u,v,w), where f(u,v)=(xu−xv)2+(yu−yv)2−−−−−−−−−−−−−−−−−−−√ and g(u,v,w)=f(u,v)+f(v,w)+f(w,u)2.
Input
There are multiple test cases. The first line of input contains an integer T, indicating the number of test cases. For each test case:
The first line contains an integer n (1≤n≤1000) – then number of points.
Each of the following n lines contains two integers xi and yi (−109≤xi,yi≤109) – coordinates of the i-th point.
Output
For each test case, output an integer denoting the answer.
Sample Input
3
3
1 1
1 1
1 1
3
0 0
0 1
1 0
1
0 0
Sample Output
4
3
0
题意
给你n个点
(
x
i
,
y
i
)
(x_i,y_i)
(xi,yi),现在问你有多少个不同的集合
S
S
S,在这个集合中存在两点
u
,
v
(
u
≠
v
)
u,v(u\neq v)
u,v(u̸=v),使得所有的
w
∋
S
w\ni S
w∋S满足下式
f
(
u
,
v
)
≥
g
(
u
,
v
,
w
)
f(u,v)\geq g(u,v,w)
f(u,v)≥g(u,v,w)
其中
f
(
u
,
v
)
=
(
x
u
−
x
v
)
2
+
(
y
u
−
y
v
)
2
f(u,v)=\sqrt{(x_u-x_v)^2+(y_u-y_v)^2}
f(u,v)=(xu−xv)2+(yu−yv)2
g
(
u
,
v
,
w
)
=
f
(
u
,
v
)
+
f
(
u
,
w
)
+
f
(
w
,
v
)
2
g(u,v,w)=\frac{f(u,v)+f(u,w)+f(w,v)}{2}
g(u,v,w)=2f(u,v)+f(u,w)+f(w,v)
思路
f
(
x
,
y
)
f(x,y)
f(x,y)就是两点之间的距离
若我们已经确定了两点,那么第三个点应该在哪呢,首先考虑w与u,v的情况,那么这三点必能构成一个三角形,假设
u
v
uv
uv为最长边为
a
=
f
(
u
,
v
)
a=f(u,v)
a=f(u,v),总周长为
L
=
f
(
u
,
v
)
+
f
(
u
,
w
)
+
f
(
w
,
v
)
L=f(u,v)+f(u,w)+f(w,v)
L=f(u,v)+f(u,w)+f(w,v)
那么有
L
−
a
=
f
(
u
,
w
)
+
f
(
w
,
v
)
L-a=f(u,w)+f(w,v)
L−a=f(u,w)+f(w,v)对于三角形的最长边来说两边之和一定大于最长边即
a
<
L
−
a
a<L-a
a<L−a
a
<
L
2
a<\frac{L}{2}
a<2L即
f
(
u
,
v
)
<
f
(
u
,
v
)
+
f
(
u
,
w
)
+
f
(
w
,
v
)
2
f(u,v)<\frac{f(u,v)+f(u,w)+f(w,v)}{2}
f(u,v)<2f(u,v)+f(u,w)+f(w,v)
与原式不符所以所有非共线的集合肯定是不符合的
再考虑共线的情况,考虑
w
w
w在
u
v
uv
uv外,假设
w
w
w在
v
v
v的外面那么有
g
(
u
,
v
,
w
)
=
f
(
u
,
v
)
+
f
(
w
,
v
)
>
f
(
u
,
v
)
g(u,v,w)=f(u,v)+f(w,v)>f(u,v)
g(u,v,w)=f(u,v)+f(w,v)>f(u,v)
所以这种情况也肯定是不行的,那么就是w在uv内部包括端点
f
(
u
,
w
)
+
f
(
w
,
v
)
=
f
(
u
,
v
)
f(u,w)+f(w,v)=f(u,v)
f(u,w)+f(w,v)=f(u,v)
有
f
(
u
,
v
)
=
g
(
u
,
v
,
w
)
=
f
(
u
,
v
)
f(u,v)=g(u,v,w)=f(u,v)
f(u,v)=g(u,v,w)=f(u,v)
那么题意就转化为共线的点集合有多少了,实际上我们找到一个共线的集合,必有两点在最两端,其他点都在这两点之内的,所以目标就是求共线点的集合数了
首先我们要对所有的点按x最小,若x相等按y小排个序,这样之后我们能保证我们遍历点的时候,第一个点总是与他共线的所有点里面的最左端的点
对于某一个点来说,若不包括这个点有n个点与其共线,可以从这n个点里面选
1
−
n
1-n
1−n个点和这个点构成集合那么方法数就有
C
n
1
+
C
n
2
+
⋯
+
C
n
n
=
2
n
−
C
n
0
=
2
n
−
1
C_n^1+C_n^2+\cdots+C_n^n=2^n-C_n^0=2^n-1
Cn1+Cn2+⋯+Cnn=2n−Cn0=2n−1
若当前选择的这个点有m个重复点那么这m个点有两种贡献一种是这m个点自己之间所构成的集合为m中选
2
−
m
2-m
2−m,即
C
m
2
+
C
m
3
+
⋯
+
C
m
n
=
2
m
−
m
−
1
C_m^2+C_m^3+\cdots+C_m^n=2^m-m-1
Cm2+Cm3+⋯+Cmn=2m−m−1
另一方面从这
m
m
m个点中选出
1
−
m
1-m
1−m个点与这n个点组合即
(
C
m
1
+
C
m
2
+
⋯
+
C
m
m
)
(
C
n
1
+
C
n
2
+
⋯
+
C
n
n
)
=
(
2
m
−
1
)
(
2
n
−
1
)
(C_m^1+C_m^2+\cdots+C_m^m)(C_n^1+C_n^2+\cdots+C_n^n)=(2^m-1)(2^n-1)
(Cm1+Cm2+⋯+Cmm)(Cn1+Cn2+⋯+Cnn)=(2m−1)(2n−1)
由于经过了排序所以重复的点的下标是连续的所以在每次求完m个点的贡献后要直接跳过m个点,避免重复计数
对于如何统计共线的点的个数呢,对于每次选取的
i
i
i点,我们遍历
i
i
i后面的点即为
j
j
j,由于直线的有一端已经固定为
i
i
i了那么对于
j
j
j来说只要记录
i
j
ij
ij的斜率就可以知道有多少个点与
i
i
i共线了,如果斜率用double存的话会有精度的问题所以可以用map中套pair的方法来记录即
map<pair<int,int>,int>
#include<bits/stdc++.h>
using namespace std;
#define X first
#define Y second
const int MAXN=1000+5;
const int mod=1e9+7;
int n;
pair<int,int>node[MAXN];
map<pair<int,int>,int>mp;
long long quickmod(long long a,long long b)
{
long long ans=1;
while(b)
{
if(b%2==1)
ans=ans*a%mod;
a=a*a%mod;
b=b/2;
}
return ans;
}
signed main()
{
int t;
scanf("%d",&t);
while(t--)
{
scanf("%d",&n);
for(int i=0; i<n; ++i)
{
int u,v;
scanf("%d%d",&u,&v);
node[i]=make_pair(u,v);
}
sort(node,node+n);
long long ans=0;
for(int i=0; i<n;)
{
long long res=1;
mp.clear();
for(int j=i+1; j<n;j++)
{
int x0=node[i].X,y0=node[i].Y,x1=node[j].X,y1=node[j].Y;
if(x0==x1&&y0==y1)
{
res++;
continue;
}
int a=y1-y0,b=x1-x0;//c=-b*y0-a*x0
int gcd=__gcd(a,b);
a/=gcd,b/=gcd;
mp[make_pair(a,b)]++;
}
if(res>1)
{
ans=(ans+quickmod(2,res)-res-1+mod)%mod;
}
for(map<pair<int,int>,int>::iterator j=mp.begin(); j!=mp.end(); j++)
{
ans=(ans+(quickmod(2,res)-1+mod)%mod*(quickmod(2,j->Y)-1+mod)%mod)%mod;
}
i+=res;
}
printf("%lld\n",ans);
}
return 0;
}
//(y1-y0)X+(x0-x1)Y+(x1y0-x0y1)=0