HDU 5738 Eureka 【共线点集】

题目链接

题意:给出n个点,每个集合中至少存在两个点,且他们都共线,问一共有多少个这样的集合。

对于在同一个位置上的k个点,在这个位置上能产生的集合数为 2kk1 2 k − k − 1

对于其他的点,遍历每个点,再遍历其他点,计算出斜率。对于每个相同的斜率,是在这个点位置上至少取一个点,再在其他点上至少取一个点。设这个点上有p个点,其他共有q个点对应于这个点能产生这个斜率,则此时 ans=ans+(2p1)(2q1) a n s = a n s + ( 2 p − 1 ) ( 2 q − 1 )

千万不要像我一样干这种对点去重对斜率不去重的傻事……

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair <ll, ll> Point;
const ll MOD = 1e9 + 7;
const ll INF = 1e11;

int T;
int n;
Point point[1010];
int cntp;
map <Point, bool> vis;
map <Point, int> cnt;
ll ans;
ll mypow[1010]; // 2^n
// map <double, bool> vis2; // 记录斜率是否存在
map <double, int> cntXielv;
double Xielv[1010];
int cntx;

void init_mypow() {
    mypow[0] = 1;
    for (int i = 1; i <= 1000; i++) {
        mypow[i] = (mypow[i - 1] * 2) % MOD;
    }
}

int main() {
    init_mypow();
    scanf("%d", &T);
    while (T--) {
        vis.clear();
        cnt.clear();
        ans = 0;
        cntp = 0;
        scanf("%d", &n);
        for (int i = 1; i <= n; i++) {
            ll x, y;
            scanf("%lld %lld", &x, &y);
            Point tp = make_pair(x, y);
            if (vis[tp]) {
                cnt[tp]++;
            }
            else {
                point[++cntp] = tp;
                vis[tp] = true;
                cnt[tp] = 1;
            }
        }
        // for (int i=1;i<=cntp;i++){
        //     printf("%lld %lld %d\n",point[i].first,point[i].second,cnt[point[i]]);
        // }
        // printf("cntp = %d\n", cntp);
        for (int i = 1; i <= cntp; i++) {
            ll cntP = cnt[point[i]];
            cntx = 0; // 记录斜率的个数
            cntXielv.clear();
            // vis2.clear();
            for (int j = i + 1; j <= cntp; j++) {
                // if (i == j)
                //     continue;
                if (point[i].first == point[j].first) {
                    Xielv[++cntx] = (double)INF;
                    cntXielv[Xielv[cntx]] += cnt[point[j]];
                }
                else {
                    double tx = (double)(point[i].second - point[j].second) / (point[i].first - point[j].first);
                    Xielv[++cntx] = tx;
                    cntXielv[Xielv[cntx]] += cnt[point[j]];
                }
            }
            sort(Xielv + 1, Xielv + cntx + 1);
            // 以上:把i这个点对应其他点的所有斜率求出+排序后放入Xielv[]中,记为Xielv[1-cntx]
            for (int j = 1; j <= cntx; j++) {
                ll cntQ = cntXielv[Xielv[j]];
                // 统计斜率为Xielv[j]的点有多少个,记为cntq
                while (Xielv[j + 1] == Xielv[j] && j + 1 <= cntx) {
                    j ++;
                    // cntQ += cntXielv[Xielv[j + 1]];
                }
                // printf("cntP = %d | cntQ = %d\n", cntP, cntQ);
                ans = (ans + (mypow[cntP] - 1) * (mypow[cntQ] - 1) % MOD + MOD) % MOD;
            }
        }
        // ans = ans / 2;
        // 加上在同一个点上的点所构成的集合
        for (int i = 1; i <= cntp; i++) {
            ll tmp = cnt[point[i]];
            // printf("point = %d %d\n",point[i].first,point[i].second);
            // printf("cnt = %d\n",tmp);
            ans = (ans + mypow[tmp] - tmp - 1 + MOD) % MOD;
        }
        ans = (ans + MOD) % MOD;
        printf("%lld\n", ans);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值