您的位置:首页 > 大数据 > 人工智能

2016 Multi-University Training Contest 2 - 1005 Eureka

2016-07-22 12:40 846 查看
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5738

题目大意:给定平面上的n个点,一个集合合法当且仅当集合中存在一对点u,v,对于集合中任意点w,均有dis(u,v)≥[dis(u,v)+dis(u,w)+dis(v,w)]/2。其中dis(A,B)为A,B两点的欧几里得距离(?)。问你这样的合法集合有多少个。数据范围:1≤n≤1000。

提示:可能出现多个同样的点,我们称之为重点。

容易得到:n个点(其中有cnt个重复的p点)共线,可构成的集合$$2^{n}-C_{n}^{1}-C_{n}^{0}\tag1$$即$2^{n}-n-1$个。包括了“任意2个p点组成集合”这种情况。

但是 同时p点也与另外m个点共线,也可构成$2^{m}-n-1$,注意 其中也包括了 “任意2个p点组成集合”。这就重复记数了。如何规避重复,这也是题目的难点所在。

这里提供一种思路;

注意到,“只选择2个重点本身组成集合”对每个不同的重点,这种情况只能计数一次(在多种共线方案中只计数一次)。其他的共线方案中,

假设 有cnt 个重点 和 n个共线的点,采用分步计数方式,第一步在重点中选1个或多个点,第二步在n个点中选1个或多个点。这样的话,种数为

$$\left( 2^{cnt}-C_{cnt}^{0}\right) \ast \left( 2^{n}-C_{n}^{0}\right)\tag2$$

神奇的是,单个点(即cnt = 1),对 2 式也成立。即1个点也看作是重点。

如何实现:“对每个不同的重点,这种情况只能计数一次(在多种共线方案中只计数一次)”呢? 其实很好实现,即对每个重点,用公式 1 计数竖直共线情况即可。

对于其他的共线方案,通过极角排序,cmp() 的时候不要把斜率化成小数比较,直接dyA*dxB<dxA*dyB这样比较可避免精度问题。(为什么要除|gcd(dx,dy)|?难道是怕爆long long?表示目前还不知道)

总结,这道题目 需要的技巧比较多,细节也比较多,很哟挑战性!

代码:

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
typedef long long LL;

const int MaxN = 1000, Pt = 1e9 + 7;
struct Point
{
int x, y, dx, dy;
} a[MaxN + 5], b[MaxN + 5];
int T, n;
LL Pow[MaxN + 5];
LL ans;

int Gcd(int x, int y)
{
if (y == 0) return x;
return Gcd(y, x % y);
}

void Init()
{
scanf("%d", &n);
Pow[0] = 1;
for (int i = 1; i <= n; i++)
{
scanf("%d%d", &a[i].x, &a[i].y);
Pow[i] = (Pow[i - 1] * 2) % Pt;
}
}

bool cmp(Point A, Point B)
{
if (A.x == B.x) return A.y < B.y;
return A.x < B.x;
}

bool cmp2(Point A, Point B)
{
return (LL)A.dy * B.dx < (LL)A.dx * B.dy;
}

void Solve()
{
ans = 0;
sort(a + 1, a + n + 1, cmp);
int L = 1, R = 1;
while (L <= n)
{
while (R < n && a[R + 1].x == a[R].x) R++;
ans = (ans + Pow[R - L + 1] - 1 - (R - L + 1)) % Pt;
int l = L, r = L;
while (l <= R)
{
while (r < R && a[r + 1].y == a[r].y) r++;
int tot = 0;
for (int i = R + 1; i <= n; i++)
{
b[++tot].dx = a[i].x - a[l].x;
b[tot].dy = a[i].y - a[l].y;
int D = Gcd(b[tot].dx, b[tot].dy);
if (D < 0) D = -D;
b[tot].dx /= D;
b[tot].dy /= D;
}
sort(b + 1, b + tot + 1, cmp2);
int cnt = 1;
for (int i = 1; i <= tot; i++)
{
if (i == tot || cmp2(b[i], b[i + 1]))
{
ans = (ans + (Pow[r - l + 1] - 1) * (Pow[cnt] - 1)) % Pt;
cnt = 1;
}
else cnt++;
}
l = r + 1;
r = r + 1;
}
L = R + 1;
R = R + 1;
}
printf("%I64d\n", ans);
}

int main()
{
scanf("%d", &T);
for (int i = 1; i <= T; i++)
{
Init();
Solve();
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: