4001 训练(二分 + count列表标记)

1. 问题描述:

达尔星有 n 个强大的下级战士,编号 1∼n。其中第 i 名战士的战斗力为 ri。战士 a 可以成为战士 b 的战斗导师,当且仅当 ra > rb 且两人之间不存在矛盾。给定每个战士的战斗力值以及战士之间存在的 k 对矛盾关系。请你计算,每个战士可以成为多少战士的战斗导师。

输入格式

第一行包含两个整数 n 和 k。第二行包含 n 个整数 r1,r2,…,rn。接下来 k 行,每行包含两个整数 x,y,表示战士 x 和战士 y 之间存在矛盾。同一对矛盾关系不会在输入中重复给出,即出现了 x,y 以后,后面就不会再次出现 x,y 或 y,x。

输出格式

共一行,n 个整数,表示每个战士可以作为战斗导师的战士数量。

数据范围

前三个测试点满足,2 ≤ n ≤ 10,0 ≤ k ≤ 10。
所有测试点满足,2 ≤ n ≤ 2 × 10 ^ 5,0 ≤ k ≤ min(2 × 10 ^ 5,n(n − 1) * 2),1 ≤ ri ≤ 10 ^ 9,1 ≤ x < y ≤n,x ≠ y。

输入样例:

4 2
10 4 10 15
1 2
4 3

输出样例:

0 0 1 2
来源:https://www.acwing.com/problem/content/description/4004/

2. 思路分析:

分析题目可以知道我们考虑当前的战士ai是其他多少个战士的导师,需要满足什么样的条件呢?我们可以考虑下面两个问题:

  • 有多少个战士的战斗力小于ra
  • 小于ra的数中有多少个是矛盾的

对于第一个问题我们可以使用二分或者双指针找到第一个小于等于ai的位置,对于第二个问题我们可以使用一个count数组来记录每一个战士的矛盾关系,对于战士x,y,如果他们有矛盾我们将矛盾记录到编号较大的战士对应的count位置,这样第一个问题的结果减去第二个问题的结果就是答案。

3. 代码如下:

from typing import List


class Solution:
    # 在a中查找小于等于x的第一个位置
    def binarySearch(self, x: int, a: List[int]):
        l, r = 0, len(a) - 1
        while l < r:
            mid = l + r >> 1
            if a[mid] >= x:
                r = mid
            else:
                l = mid + 1
        return r

    def process(self):
        n, m = map(int, input().split())
        a = list(map(int, input().split()))
        # 因为后面需要二分所以需要拷贝a的副本到b中
        b = a[:]
        N = 3 * 10 ** 5
        # count用来记录某个点矛盾的数量
        count = [0] * N
        b.sort()
        # 将矛盾的数目记录在count中
        for i in range(m):
            x, y = map(int, input().split())
            if a[x - 1] > a[y - 1]:
                count[x] += 1
            elif a[x - 1] < a[y - 1]:
                count[y] += 1
        # 枚举所有的战士判断当前的战士是其他多少个战士的导师
        for i in range(1, n + 1):
            x = a[i - 1]
            k = self.binarySearch(x, b)
            print(k - count[i], end=" ")


if __name__ == '__main__':
    Solution().process()
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值