4202. 穿过圆 (python)

把整个坐标系想象成一个大圈,编号就给他当m,先把能括住每个点的最小圈记录下来,再把能括住某个圈的最小圈记录下来,这样每个点都有父圈,每个小圈也有父圈。这样每次询问a和b的距离,可以通过计数经过的父圈节点来计算距离。怎么快速计算到父圈的距离呢?通过创建st表,st[i][j]表示编号i点经过2**j的父圈到达的父父父....圈编号。但是k=10**5次方,算法超时。有待改进!!!!!!!!!!!!!

from collections import defaultdict

def dfs(node, f):
    st[node][0] = f
    depth[node] = depth[f]+1
    for j in range(1, 12):
        st[node][j] = st[ st[node][j-1] ][j-1]
    for nex in edge[node]:
        dfs(nex, node)
        
n, m, k = map(int, input().split())
points = []
cir = []
for _ in range(n):
    x, y = map(int, input().split())
    points.append([x, y])
for _ in range(m):
    r, x, y = map(int, input().split())
    cir.append([r, x, y])
cir.sort()
# 圆形半径是从小到大排序, 所以可以记录能包括某个点的最小圆
min_c = [m]*n
for i in range(n):
    x, y = points[i]
    for j in range(m):
        r, x1, y1 = cir[j]
        if (x1-x)**2 + (y1-y)**2 < r**2:
            min_c[i] = j
            break
# 给圆形建立树,大圆扩小圆
# edge[i] 表示编号i的子节点
edge = defaultdict(list)
for i in range(m):
    r1, x1, y1 = cir[i]
    b = True
    for j in range(i+1, m):
        r2, x2, y2 = cir[j]
        #print(i, cir[j] )
        if (x1-x2)**2 + (y1-y2)**2 < r2**2:
            edge[j].append(i)
            b = False
            break
    if b: edge[m].append(i)
#print(edge)
# 建立st表,st[i][j]表示从i号圈向外走2**j个圈,的编号
st = [ [0]*12 for _ in range(m+1) ]
depth = [0]*(m+1)
depth[m] = 0

dfs(m, m)
for _ in range(k):
    a, b = map(int, input().split())
    a, b = min_c[a-1], min_c[b-1]
    if a == b:
        print(0)
        continue
    ans = 0
    d1, d2 = depth[a], depth[b]
    # 让a节点更深,方便处理
    if d1 < d2:
        d1, d2 = d2, d1
        a, b = b, a
    # 让a节点到达b节点的深度
    ans += d1-d2
    for i in range(11, -1, -1):
        if depth[st[a][i]] >= d2:
            a = st[a][i]
    if a == b:
        print(ans)
        continue
    # a与b不在同一个圈中,找到LCA
    for i in range(11, -1, -1):
        if st[a][i] != st[b][i]:
            a = st[a][i]
            b = st[b][i]
            ans += 2**(i+1)
    ans += 2
    print(ans)

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值