A.Sorting with Twos
题目
给定一个数组a,可执行操作如下:
1、选择一个非负整数m,要求
2、将数组中元素从1到m减去1
问,是否可以通过以上操作得到一个单调不增的数组序列。
输入
首行样例个数t,
每个样例首行数组长度n,
第二行数组a,其中
输出
"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,其中 。如果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中元素能够被整除,则将此元素加上。执行完q个操作后,输出数组a。
输入
首行样例个数t,
每个样例首行数组长度n和q,
第二行数组a,其中
第三行数组x,
输出
每个样例修改后的数组a
解析
命题人的解析很巧妙,我当时对x去重后直接模拟的,也能通过。如果一个数字a能够被 整除,那么一定不能被整除(纸上写个公式就明白了),同时之后也不能被比大的整除(因为的加入)。因此,数组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个部落有个兽人,游戏目标击败所有部落。玩家有两种攻击方式和一个怒气值x,初始为0:
1、选择一个部落,击败一个兽人,怒气加一
2、选择一个至少存在x个兽人的部落,消耗所有怒气,击败x个兽人
问最少需要多少次攻击可以达到游戏目标
输入
首行样例个数t,
每个样例首行数部落数n,
第二行部落数组a,其中
保证样例n的总和不超过
输出
最小攻击次数
解析
贪心算法,将数组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两次。CF灰692->CFCF灰938。