题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5738
题目给我们的形成目标集合的条件很好理解,因为两点之间线段最短,所以能够达到这个条件很明显就是说点广告好就在这个线段上(注意,含端点,题上没有说u,v,w不相等,也就是说任意两个点都可以形成这个条件),那么题目就变成有多少这种组合,首先如果n个点和我们枚举到的点都在一个位置上,那么在这个位置上和当前点组成集合的总数就是2^n-1(除去一个点都不取的情况),如果是与枚举到的点在一条直线上有m个点,那么总数就是2^n * (2^m-1)。所以我们只需要对点按x,y排个序,然后n^2枚举就可以得到答案。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn = 1000+5;
const int MOD = 1e9+7;
typedef long long LL;
LL gcd(LL a,LL b){return b?gcd(b,a%b):a;}
struct Point{
LL x,y;
Point() {}
Point(LL _x,LL _y): x(_x),y(_y) {}
Point operator - (const Point &a) const {
return Point(x-a.x, y-a.y);
}
bool operator < (const Point &a) const {
return x < a.x || (x==a.x&&y < a.y);
}
bool operator == (const Point &a) const {
return x==a.x && y==a.y;
}
void reduce()
{
int g = gcd(x,y);
if(g) x /= g,y /= g;
}
}point[maxn],vect[maxn];
LL pw[maxn];
void update(LL &x,LL y)
{
x += y;
if(x > MOD) x -= MOD;
}
int main()
{
//freopen("1005.in", "r", stdin);
pw[0] = 1;
for(int i=1; i<=1000; i++) pw[i] = (pw[i-1]*2)%MOD;
int T;
scanf("%d", &T);
while(T--)
{
int n,cnt,num;
scanf("%d", &n);
for(int i=0; i<n; i++)
scanf("%I64d%I64d", &point[i].x, &point[i].y);
sort(point, point+n);
LL ans = 0;
for(int i=0; i<n; i++)
{
cnt = num = 0;
for(int j=i+1; j<n; j++)
{
if(point[i] == point[j]) cnt++;
else
{
vect[num] = (point[j]-point[i]);
vect[num++].reduce();
}
}
update(ans, pw[cnt] - 1);
sort(vect, vect+num);
for(int j=0,k; j<num; j=k)
{
for(k=j; k<num&&vect[j]==vect[k]; k++);
update(ans, pw[cnt]*(pw[k-j]-1) % MOD);
}
}
printf("%I64d\n", ans);
}
}