zoukankan      html  css  js  c++  java
  • 2016 Multi-University Training Contest 2

    题目链接: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} ag1$$即$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} ight) ast left( 2^{n}-C_{n}^{0} ight) ag2$$

    神奇的是,单个点(即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
    ", ans);
    }
    
    int main()
    {
        scanf("%d", &T);
        for (int i = 1; i <= T; i++)
        {
            Init();
            Solve();
        }
    }
  • 相关阅读:
    使用Project Linker 2012实现多个项目中代码文件
    使用的开放源码Scryber库动态生成PDF文件
    破解Excel 密码保护
    入围51CTO 2009年度“最受读者欢迎的IT图书作者”评选
    谈谈分布式事务之二:基于DTC的分布式事务管理模型[下篇]
    WCF 技术剖析之三十三:你是否了解WCF事务框架体系内部的工作机制?[下篇]
    事件(Event),绝大多数内存泄漏(Memory Leak)的元凶[下篇] (提供Source Code下载)
    如何实现对上下文(Context)数据的统一管理 [提供源代码下载]
    WCF技术剖析之二十七: 如何将一个服务发布成WSDL[基于HTTPGET的实现](提供模拟程序)
    Enterprise Library深入解析与灵活应用(9):个人觉得比较严重的关于CachingCallHandler的Bug
  • 原文地址:https://www.cnblogs.com/shawn-ji/p/5694760.html
Copyright © 2011-2022 走看看