2024牛客寒假训练营第6场—H题解

2024 牛客寒假训练营(6)— H 题题解。

题目链接

思路

首先很容易就想到一个最暴力的做法:
  1. g [ i ] [ j ] g[i][j] g[i][j] 表示点 i i i 到点 j j j 这段线段与圆的交点数。
  2. 我们可以枚举构成三角形的三个点 i 、 j 、 k , ( 1 ≤ i < j < k ≤ n ) i、j、k,(1 \le i < j < k \le n) ijk(1i<j<kn) ,统计三角形边上的点数之和。
  3. s u m = ∑ 1 ≤ i < j < k ≤ n g [ i ] [ j ] + g [ i ] [ k ] + g [ j ] [ k ] sum = \sum _{1 \le i < j < k\le n} g[i][j] + g[i][k] + g[j][k] sum=1i<j<kng[i][j]+g[i][k]+g[j][k]
  4. sum 是我们的答案吗?
  5. 并不是
  6. 如果一个点恰好在圆上,则按照上面的做法,每枚举一个包含这个点的三角形,就会重复计算一次这个点,所以我们要减去重复计算的部分
  7. a n s = s u m − ∑ 1 ≤ i ≤ n ( n − 1 ) ∗ ( n − 2 ) / 2 ∗ ( d i s t [ i ] = = r ) ans = sum - \sum_{1 \le i \le n} (n-1)*(n-2)/2 * (dist[i] == r) ans=sum1in(n1)(n2)/2(dist[i]==r) d i s t [ i ] dist[i] dist[i] i i i 到圆心的距离。
  8. 复杂度 O ( n 3 ) O(n^3) O(n3)
怎么优化呢?
  1. ​ 我们考虑每个 g [ i ] [ j ] g[i][j] g[i][j] 在上面第 3 3 3 步中加了多少次,当 i , j i,j i,j 这两个点确定选之后,只要再选一个点即可,包含 g [ i ] [ j ] g[i][j] g[i][j] 的三角形有 n − 2 n-2 n2 个,所以 g [ i ] [ j ] g[i][j] g[i][j] 被加了 n − 2 n-2 n2 次。
  2. 所以 s u m = ∑ 1 ≤ i < j ≤ n g [ i ] [ j ] ∗ ( n − 2 ) sum = \sum_{1\le i < j \le n} g[i][j] * (n-2) sum=1i<jng[i][j](n2)
  3. 复杂度 O ( n 2 ) O(n^2) O(n2)
怎么求线段与圆的交点数量?

设线段端点是 p 1 , p 2 p1,p2 p1,p2,到圆心的距离是 d 1 , d 2 d1,d2 d1,d2,圆心到直线的距离为 d d d p 1 − p 2 直线方程为 l 1 : A x + B y + C = 0 p1-p2直线方程为 l_1:Ax + By + C = 0 p1p2直线方程为l1:Ax+By+C=0,圆心坐标是 ( a , b ) (a,b) (a,b)。过圆心与 p 1 − p 2 p1-p2 p1p2 垂直的直线为 l 2 : B x − A y + A b − B a = 0 l_2:Bx - Ay + Ab - Ba = 0 l2:BxAy+AbBa=0

  1. 如果两个点都在圆的内部,则没有交点。if (d1 < r&& d2 < r) return 0;
  2. 若一个点在圆内部,另一个点不在圆内部,则有一个交点。else if(d1 < r || d2 < r) return 1;
  3. 若没有点在圆的内部:
    • d > r d > r d>r ,则没有交点。
    • d = r d = r d=r ,则检查 p 1 、 p 2 p1、p2 p1p2 是否分布在 l 2 l_2 l2 两边(或者有一个点距离等于 r r r),若是则有 1 1 1 个交点,若不是则没有交点。
    • d < r d < r d<r​ ,则检查 p 1 、 p 2 p1、p2 p1p2 是否分布在 l 2 l_2 l2 两边,若是则有 2 2 2 个交点,若不是则检查有没有点在圆上,若有则有一个交点,反之没有交点。

Ac_code

import math
N = 1010
def dist(a, b):
    return (a[0] - b[0]) ** 2 + (a[1] - b[1]) ** 2
def dist_line(A, B, C, x, y):
    up = abs(A * x + B * y + C)
    up *= up
    down = A * A + B * B
    return up, down
def f(A, B, C, x, y):
    return A * x + B * y + C
def count(x1, y1, x2, y2, a, b, r):
    d1 = dist((a, b), (x1, y1))
    d2 = dist((a, b), (x2, y2))
    if d1 < r*r and d2 < r*r:
        return 0
    elif d1 < r*r or d2 < r*r:
        return 1
    else:
        A = y2 - y1
        B = x1 - x2
        C = y1 * (x2 - x1) + x1 * (y1 - y2)
        up, down = dist_line(A, B, C, a, b)
        if up > down * r * r:
            return 0
        nA = B
        nB = -A
        nC = A * b - B * a
        if up == down * r * r:
            if f(nA, nB, nC, x1, y1) * f(nA, nB, nC, x2, y2) <= 0:
                return 1
            return 0
        if f(nA, nB, nC, x1, y1) * f(nA, nB, nC, x2, y2) > 0:
            if d1 == r * r or d2 == r * r:
                return 1
            return 0
        else:
            return 2
def main():
    global a, b, r
    a, b, r = map(int, input().split())
    n = int(input())
    p = [tuple(map(int, input().split())) for _ in range(n)]
    ans = 0
    for i in range(n):
        for j in range(i + 1, n):
            x1, y1 = p[i]
            x2, y2 = p[j]
            ans += count(x1, y1, x2, y2, a, b, r)
    cnt = (n - 1) * (n - 2) // 2
    ans *= (n - 2)
    for i in range(n):
        if dist(p[i], (a, b)) == r * r:
            ans -= cnt
    down = n * (n - 1) * (n - 2) // 6
    print('{:.20f}'.format(1.0 * ans / down))
if __name__ == "__main__":
    main()
  • 23
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值