Codeforces Round 907 (Div. 2——ABC)

A.Sorting with Twos

题目

 给定一个数组a,可执行操作如下:

1、选择一个非负整数m,要求2^{m} \leq n

2、将数组中元素从1到m减去1

问,是否可以通过以上操作得到一个单调不增的数组序列。

输入

首行样例个数t,1 \leq t \leq 10^{4}

每个样例首行数组长度n,1 \leq n \leq 20

第二行数组a,其中0 \leq a_{i} \leq 1000

输出

"YES" or "NO"

解析

减一的操作执行范围是一个区间如,1-1,1-2,1-4,1-8...。每个区间中的数字比能通过减一操作,使其全部小于区间后的数字,故判断数组是否可以成为单调不增序列,主要判断两个区间之间,一同变化的数字时候存在逆序。

比如1-2和1-4,如果3,4存在逆序结果则为NO,因为3和4是一同变化的,无论怎样变化都改变不了二者的大小关系。1-4和1-8类似,查看5-8之间是否存在逆序。

先得到逆序数组b,其中b_{i} = a_{i+1} - a_{i} 。如果i不是2的整次幂,且为负数,说明此处存在无法改变的逆序,则输出结果为NO。

(difference array不应该翻成“差分数组”吧?至少我学差分的时候,不是现在这个概念。有懂的小伙伴可以评论说下。)

代码


T = int(input().strip())

for t in range(T):
    n = int(input().strip())
    N = n + 10
    a = [0] * N
    b = a.copy()
    a[1:n+1] = list(map(int, input().strip().split()))

    for i in range(1, n):
        b[i] = a[i+1] - a[i]

    for i in range(1, n+1):
        if bin(i).count("1") != 1 and b[i] < 0:
            print("NO")
            break
    else:
        print("YES")

 如何判断i是2的整次幂,我第一想到的是位运算。因为是python,我直接转成了二进制字符串计算“1”的数量。如果一个数字是2的整次幂,则其二进制表示中1的数量应该为1。自己写个计数函数提前break应该会快一点。


def pow_2(k):
    cnt1 = 0
    while k:
        if k & 1:
            cnt1 += 1
            if cnt1 > 1:
                return False
        k >>= 1
    return True

T = int(input().strip())

for t in range(T):
    n = int(input().strip())
    N = n + 10
    a = [0] * N
    b = a.copy()
    a[1:n+1] = list(map(int, input().strip().split()))

    for i in range(1, n):
        b[i] = a[i+1] - a[i]

    for i in range(1, n+1):
        if not pow_2(i) and b[i] < 0:
            print("NO")
            break
    else:
        print("YES")

数据范围较小,发现两种写法时间差不多。 

B.Deja Vu(法语,大概“似曾相识”的意思)

题目

 给定一个长度为n的数组a,和长度为q的数组x,其中元素为整数。执行q个操作,对于第i个操作,如果a中元素能够被2^{x_{i}}整除,则将此元素加上2^{x_{i-1}}。执行完q个操作后,输出数组a。

输入

首行样例个数t,1 \leq t \leq 10^{4}

每个样例首行数组长度n和q,1 \leq n, q \leq 10^{5}

第二行数组a,其中0 \leq a_{i} \leq 10^{9}

第三行数组x,0 \leq x_{i} \leq 30

输出

每个样例修改后的数组a

解析

命题人的解析很巧妙,我当时对x去重后直接模拟的,也能通过。如果一个数字a能够被x_{i} 整除,那么a + x_{i-1}一定不能被x_{i}整除(纸上写个公式就明白了),同时之后也不能被比x_{i}大的x_{j}整除(因为x_{i-1}的加入)。因此,数组x可以简化成严格递减的单调队列,降低复杂度。

代码

rank时:

T = int(input().strip())

for t in range(T):
    n, q = map(int, input().strip().split())
    a = list(map(int, input().strip().split()))
    x = list(map(int, input().strip().split()))
    x_c = list()
    x_s = set()
    for xi in x:
        if xi not in x_s:
            x_c.append(xi)
            x_s.add(xi)
    x = x_c

    for i in range(len(x)):
        div = 1
        for k in range(x[i]):
            div <<= 1
        add = div >> 1
        for j in range(n):
            if a[j] % div == 0:
                a[j] += add

    for i in a:
        print(i, end=" ")
    print()

按照命题人解析思路:

T = int(input().strip())

for t in range(T):
    n, q = map(int, input().strip().split())
    a = list(map(int, input().strip().split()))
    x = list(map(int, input().strip().split()))
    temp = list()

    for xi in x:
        if not temp or xi < temp[-1]:
            temp.append(xi)
    x = temp

    for xi in x:
        div = 1
        for k in range(xi):
            div <<= 1
        for i in range(len(a)):
            if a[i] % div == 0:
                a[i] += (div >> 1)

    for i in a:
        print(i, end=" ")
    print()

最后发现时间差不多,理论上命题人的要更快,因为单调递减队列元素肯定要比简单去重少。(可能是不同时间提交的原因吧。) 

C.Smilo and Monsters

题目

有n个兽人部落,第i个部落有a_{i}个兽人,游戏目标击败所有部落。玩家有两种攻击方式和一个怒气值x,初始为0:

1、选择一个部落,击败一个兽人,怒气加一

2、选择一个至少存在x个兽人的部落,消耗所有怒气,击败x个兽人

问最少需要多少次攻击可以达到游戏目标

输入

首行样例个数t,1 \leq t \leq 10^{4}

每个样例首行数部落数n,1 \leq n \leq 2 \times 10^{5}

第二行部落数组a,其中0 \leq a_{i} \leq 10^{9}

保证样例n的总和不超过2 \times 10^{5}

输出

最小攻击次数

解析

 贪心算法,将数组a排序,用小部落积累怒气,处理大部落。如果总兽人数为b,那么有b // a数量的兽人可以用怒气清楚,故怒气释放次数为b // a 所能覆盖的最大部落的数量,再加上积攒怒气的次数,即为最终结果。

比赛时贪心想到了,模拟TLE,还TLE了两次。

代码

from math import ceil

T = int(input().strip())

for t in range(T):
    n = int(input().strip())
    a = list(map(int, input().strip().split()))
    a.sort()

    b = sum(a)
    i = n
    c = b // 2
    cnt = 0
    while c > 0:
        i -= 1
        c -= a[i]
    
    print(ceil(b / 2) + n - i)

题目阅读依旧是一个问题,尤其题目B,看懂题看了半天,愁。

AB一次过,CTLE两次。CF692->CFCF938。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值