2016 Multi-University Training Contest 2 - 1005 Eureka

題目連接:http://acm.hdu.edu.cn/showproblem.php?pid=5738php

題目大意:給定平面上的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。spa

 

提示:可能出現多個一樣的點,咱們稱之爲重點。code

容易獲得:n個點(其中有cnt個重複的p點)共線,可構成的集合$$2^{n}-C_{n}^{1}-C_{n}^{0}\tag1$$即$2^{n}-n-1$個。包括了「任意2個p點組成集合」這種狀況。blog

可是 同時p點也與另外m個點共線,也可構成$2^{m}-n-1$,注意 其中也包括了 「任意2個p點組成集合」。這就重複記數了。如何規避重複,這也是題目的難點所在。排序

這裏提供一種思路;get

注意到,「只選擇2個重點自己組成集合」對每一個不一樣的重點,這種狀況只能計數一次(在多種共線方案中只計數一次)。其餘的共線方案中,string

假設 有cnt 個重點 和 n個共線的點,採用分步計數方式,第一步在重點中選1個或多個點,第二步在n個點中選1個或多個點。這樣的話,種數爲it

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

神奇的是,單個點(即cnt = 1),對 2 式也成立。即1個點也看做是重點。ast

 

如何實現:「對每一個不一樣的重點,這種狀況只能計數一次(在多種共線方案中只計數一次)」呢?   其實很好實現,即對每一個重點,用公式 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();
    }
}
相關文章
相關標籤/搜索