2015ACM/ICPC亚洲区上海站 Infinity Point Sets

本文探讨了一个关于点集连线的问题,给出了详细的算法实现过程。通过分析不同数量的点及其排列方式,提出了一种求解特定条件下无法无限连线的子点集的方法。利用C++实现了算法,并考虑了多种特殊情况。

题意:每个点两两连线,线的交点可以继续连线,给n个点,求其所有子点集不能无限画线的方法。

大致思路:在纸上画画,大概可以分成以下几种情况

  1. 4个点及以下
  2. 5点及以上共线
  3. 4点及以上共线 + 线外一点
  4. 3点及以上共线 + 线两侧各取一点
#include<bits/stdc++.h>
using namespace std;
using namespace std;

const int MAXN = 1111;
const int mod = 1e9 + 7;
const int inv2 = 5e8 + 4;
typedef long long ll;
struct point {
    ll x, y;
    double a;
    point (ll x = 0, ll y = 0) :x(x), y(y), a(atan2(y, x)) {}
    bool operator < (const point &rhs) const {
        return a < rhs.a;
    }
}p[MAXN << 1];
ll dot(point a, point b) {
    return a.x * b.x + a.y * b.y;
}
ll cross(point a, point b) {
    return a.x * b.y - a.y * b.x;
}
ll C[MAXN][MAXN], CC[MAXN][MAXN], dia[MAXN];
int x[MAXN], y[MAXN];
int icase, t, n, ans;
void init() {
    CC[0][0] = C[0][0] = 1;
    for (int i = 1; i < MAXN; i++)
        for (int j = 0; j <= i; j++)
            if (j == 0) CC[i][j] = C[i][j] = 1;
            else {
                C[i][j] = (C[i - 1][j] + C[i- 1][j - 1]) % mod;
                CC[i][j] = (C[i][j] + CC[i][j - 1]) % mod;
            }
}
void solve(int id) {
    ll m = 0, d = 0, sum = 0;
    for (int i = 0; i < n; i++) if (i != id) p[m++] = point(x[i] - x[id], y[i] - y[id]);
    sort(p, p + m);
    for (int i = 0; i < m; i++) p[i + m] = p[i];
    for (int i = 0, j = 0, k = 0; i < m; i = j) {
        while (j < i + m && cross(p[i], p[j]) == 0 && dot(p[i], p[j]) > 0) j++;
        if (j > k) k = j;
        while (k < i + m && cross(p[i], p[k]) > 0) k++;
        if (k < i + m && cross(p[i], p[k]) == 0) {
            if (i > k % m) continue;
            int cnt = 0;
            while (k < i + m && cross(p[i], p[k]) == 0) ++k, ++cnt;
            dia[d++] = 1ll * cnt * (j - i) % mod;
            sum = (sum + dia[d - 1]) % mod;
        }
        else {
            int cnt = j - i + 1, l = k - j, r = i + m - k;
            if (cnt >= 3) ans = (ans + 1ll * (CC[cnt][cnt] - CC[cnt][2] + mod) % mod * l % mod * r % mod) % mod;
            if (cnt >= 4) ans = (ans + 1ll * (CC[cnt][cnt] - CC[cnt][3] + mod) % mod * (l + r) % mod) % mod;
            if (cnt >= 5) ans = (ans + (CC[cnt][cnt] - CC[cnt][4] + mod) % mod) % mod;
        }
    }
    for (int i = 0; i < d; i++) ans = (ans + mod - 1ll * dia[i] * (sum + mod - dia[i]) % mod) % mod;
}
int main() {
    init();
    scanf("%d", &t);
    while (t--) {
        scanf("%d", &n);
        for (int i = 0; i < n; i++) scanf("%d%d", x + i, y + i);
        ans = 0;
        for (int i = 0; i < n; i++) solve(i);
        ans = 1ll * ans * inv2 % mod;
        for (int i = 1; i <= 4; i++) ans = (ans + C[n][i]) % mod;
        printf("Case #%d: %lld\n", ++icase, ans);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值