UVa1359/LA3491 Hills

题目链接

         本题是2005年ICPC亚洲区域赛杭州欧赛区H

题意

        平面上有 n(n≤500)条线段,其中每条线段的端点都不会在其他线段上。你的任务是数一数有多少个“没有被其他线段切到”的三角形(即小山)。如下图所示,虽然有两个三角形,但其中一个被切到了,所以答案是1。

分析

        将每条线段作为两条有向线段做预处理:依次与其他线段求交点(用分数保存叉乘比值)同时保存逆时针夹角的余弦值,最后对多线段交于同一点时保留余弦值最小的那一段。

        预处理后,遍历并计数:依次遍历有向线段的每一最小分段,当恰好时三条不同线段的最小分段形成环时计数+1。答案是计数结果除以3。

AC代码

#include <iostream>
#include <cmath>
#include <algorithm>
using namespace std;

#define N 504
int x[N], y[N], vx[N], vy[N], c[2][N], n; double l[N];
struct frac {
    int p, q;
    bool operator== (const frac& rhs) const {
        return p*(long long)rhs.q - q*(long long)rhs.p == 0;
    }
};

struct je {
    frac f, e; double t; int i;
    bool operator< (const frac& r) const {
        return f.p*(long long)r.q - f.q*(long long)r.p < 0;
    }
    bool operator< (const je& rhs) const {
        return f.p*(long long)rhs.f.q - f.q*(long long)rhs.f.p < 0;
    }
} a[2][N][N];

void merge(je (&a)[N], int &c) {
    sort(a, a+c);
    int k = 0;
    for (int j=1; j<c; ++j) {
        if (a[k] < a[j]) a[++k] = a[j];
        else if (a[j].t < a[k].t) a[k].e = a[j].e, a[k].t = a[j].t, a[k].i = a[j].i;
    }
    c = ++k;
}

int solve() {
    cin >> n;
    for (int i=0; i<n; ++i) {
        cin >> x[i] >> y[i] >> vx[i] >> vy[i];
        c[0][i] = c[1][i] = 0; vx[i] -= x[i]; vy[i] -= y[i];
        l[i] = sqrt(vx[i]*vx[i] + vy[i]*vy[i]);
    }
    for (int i=0; i<n; ++i) {
        for (int j=i+1; j<n; ++j) {
            int ux = x[i] - x[j], uy = y[i] - y[j];
            int p1 = vx[j]*uy - ux*vy[j], p2 = vx[i]*uy - ux*vy[i], q0 = vx[i]*vy[j] - vx[j]*vy[i], q = q0;
            if (q < 0) p1 = -p1, p2 = -p2, q = -q;
            if (p1 < 0 || p1 > q || p2 < 0 || p2 > q) continue;
            double t = (vx[i]*vx[j] + vy[i]*vy[j]) / l[i] / l[j];
            je &ef = a[0][i][c[0][i]++], &eb = a[1][i][c[1][i]++], &ff = a[0][j][c[0][j]++], &fb = a[1][j][c[1][j]++];
            ef.f.q = eb.f.q = ff.f.q = fb.f.q = q; ef.f.p = p1; eb.f.p = q-p1; ff.f.p = p2; fb.f.p = q-p2;
            if (q0 > 0) {
                ef.e = ff.f; eb.e = fb.f; ef.t = eb.t = t; ef.i = j; eb.i = j+n;
                ff.e = eb.f; fb.e = ef.f; ff.t = fb.t = -t; ff.i = i+n; fb.i = i;
            } else {
                ef.e = fb.f; eb.e = ff.f; ef.t = eb.t = -t; ef.i = j+n; eb.i = j;
                ff.e = ef.f; fb.e = eb.f; ff.t = fb.t = t; ff.i = i; fb.i = i+n;
            }
        }
        merge(a[0][i], c[0][i]); merge(a[1][i], c[1][i]);
    }
    int s = 0;
    for (int i=0; i<n; ++i) for (int j=0; j<2; ++j) for (int k=1; k<c[j][i]; ++k) {
        je &e = a[j][i][k]; int r = e.i < n ? 0 : 1, m = e.i < n ? e.i : e.i-n, t = c[r][m];
        je (&p)[N] = a[r][m]; int x = lower_bound(p, p+t, e.e) - p + 1;
        if (x == t) continue;
        je &b = p[x]; r = b.i < n ? 0 : 1; m = b.i < n ? b.i : b.i-n; t = c[r][m];
        je (&u)[N] = a[r][m]; x = lower_bound(u, u+t, b.e) - u + 1;
        if (x < t && u[x].i == j*n+i && u[x].e == a[j][i][k-1].f) ++s;
    }
    return s / 3;
}

int main() {
    int t; cin >> t;
    for (int kase=1; kase<=t; ++kase) {
        cout << "Case " << kase << ':' << endl << solve() << endl;
        if (kase < t) cout << endl;
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值