2024年蓝桥杯pythonB组真题

穿越时空之门

问题描述

随着 20242024 年的钟声回荡,传说中的时空之门再次敞开。这扇门是一条神秘的通道,它连接着二进制和四进制两个不同的数码领域,等待着勇者们的探索。

在二进制的领域里,勇者的力量被转换成了力量数值的二进制表示中各数位之和。

在四进制的领域里,力量的转换规则相似,变成了力量数值的四进制表示中各数位之和。

穿越这扇时空之门的条件是严苛的:当且仅当勇者在二进制领域的力量等同于四进制领域的力量时,他才能够成功地穿越。

国王选定了小蓝作为领路人,带领着力量值从 11 到 20242024 的勇者们踏上了这段探索未知的旅程。作为小蓝的助手,你的任务是帮助小蓝计算出,在这 20242024 位勇者中,有多少人符合穿越时空之门的条件。

答案提交

这是一道结果填空题,你只需要算出结果后提交即可。本题的结果为一个整数,在提交答案时只填写这个整数,填写多余的内容将无法得分。

ans = 0
 
def check(x): # 判断是否符合条件
    s1, s2 = sum(int(i) for i in bin(x)[2:]), 0
    while x:
        s2 += x % 4
        x //= 4
    return s1 == s2
 
for i in range(1, 2025):
    ans += check(i)
 
print(ans) # 63

数字串个数

问题描述

小蓝想要构造出一个长度为 100001000010000 的数字字符串,有以下要求:

  1. 小蓝不喜欢数字 000,所以数字字符串中不可以出现 000;

  2. 小蓝喜欢数字 333 和 777,所以数字字符串中必须要有 333 和 777 这两个数字。

请问满足题意的数字字符串有多少个?这个数字会很大,你只需要输出其对 109+710^9+7109+7 取余后的结果。

答案提交

这是一道结果填空的题,你只需要算出结果后提交即可。本题的结果为一个整数,在提交答案时只填写这个整数,填写多余的内容将无法得分。

mod = 10**9 + 7
 
# 157509472
print((pow(9, 10000, mod) - 2 * pow(8, 10000, mod) + pow(7, 10000, mod)) % mod)

连连看

问题描述

小蓝正在和朋友们玩一种新的连连看游戏。在一个 n×mn \times mn×m 的矩形网格中,每个格子中都有一个整数,第 iii 行第 jjj 列上的整数为 Ai,jA_{i, j}Ai,j 。玩家需要在这个网格中寻找一对格子 (a,b)−(c,d)\left( {a, b}\right) - \left( {c, d}\right)(a,b)(c,d) 使得这两个格子中的整数 Aa,bA_{a, b}Aa,bAc,dA_{c, d}Ac,d 相等,且它们的位置满足 ∣a−c∣=∣b−d∣>0\left| {a - c}\right| = \left| {b - d}\right| > 0ac=bd>0 。请问在这个 n×mn \times mn×m 的矩形网格中有多少对这样的格子满足条件。

输入格式

输入的第一行包含两个正整数 n,mn, mn,m,用一个空格分隔。

接下来 nnn 行,第 iii 行包含 mmm 个正整数 Ai,1,Ai,2,⋯ ,Ai,mA_{i,1},A_{i,2},\cdots ,A_{i, m}Ai,1,Ai,2,,Ai,m,相邻整数之间使用一个空格分隔。

输出格式

输出一行包含一个整数表示答案。

样例输入

3 2 1 2 2 3 3 2

样例输出

6

样例说明

一共有以下 666 对格子:(1,2)−(2,1)\left( {1,2}\right) - \left( {2,1}\right)(1,2)(2,1)(2,2)−(3,1)\left( {2,2}\right) - \left( {3,1}\right)(2,2)(3,1)(2,1)−(3,2)\left( {2,1}\right) - \left( {3,2}\right)(2,1)(3,2)(2,1)−(1,2)\left( {2,1}\right) - \left( {1,2}\right)(2,1)(1,2)(3,1)−(2,2)\left( {3,1}\right) - \left( {2,2}\right)(3,1)(2,2)(3,2)−(2,1)\left( {3,2}\right) - \left( {2,1}\right)(3,2)(2,1)

评测用例规模与约定

对于 20%{20}\%20% 的评测用例,1≤n,m≤501 \leq n, m \leq {50}1n,m50

对于所有评测用例,1≤n,m≤1000,1≤Ai,j≤10001 \leq n, m \leq {1000},1 \leq {A}_{i, j} \leq {1000}1n,m1000,1Ai,j1000

解法1

import sys
input = lambda: sys.stdin.readline().strip()    # 快读
n, m = map(int, input().split())
maps = []
hax0 = [[0]*1001 for i in range(2001)]  # 斜行=> '\'
hax1 = [[0]*1001 for i in range(2001)]  # 斜行=> '/'
for i in range(n):
    maps.append(list(map(int, input().split())))
    
    # 记录这一斜行每个元素出现次数
    for j, x in enumerate(maps[-1]):
        hax0[(i-j) % 2000][x] += 1
        hax1[(i+j) % 2000][x] += 1
res = 0
for i in range(n):
    for j in range(m):
        ai = maps[i][j]

        # 因为要排除自己,所以需要大于1才行,还需要-1。
        # 例如当前斜行有2个相等元素,有一个是自己,所以只能配成一对
        if hax0[(i-j) % 2000][ai] > 1:
            res += hax0[(i-j) % 2000][ai] - 1
        if hax1[(i+j) % 2000][ai] > 1:
            res += hax1[(i+j) % 2000][ai] - 1
print(res)

解法2

from collections import *
 
n, m = map(int, input().split())
a = [list(map(int, input().split())) for _ in range(n)]
 
def acc(a): # 统计从左上到右下方向上的格子对数量
    res = 0
    for k in range(-(n - 1), m):
        i, j = -k * (k < 0), k * (k > 0) # 该方向上第一个位置
        c = Counter()
        while i < n and j < m:
            c[a[i][j]] += 1
            i += 1; j += 1
        for v in c.values():
            res += v * (v - 1)
    return res
 
print(acc(a) + acc([a[i][::-1] for i in range(n)]))

超时

n, m = map(int, input().split())
A = [list(map(int, input().split())) for _ in range(n)]
 
# A[a][b] = A[c][d], 处于同一斜线上
cnt = 0
for i in range(n):
    for j in range(m):
        # 只跟当前行以下的行比较
        # 向左下角
        for p in range(1, min(n - i, j + 1)):
            cnt += A[i][j] == A[i + p][j - p]
        # 向右下角
        for p in range(1, min(n - i, m - j)):
            cnt += A[i][j] == A[i + p][j + p]
print(cnt * 2)

通过

n, m = map(int, input().split())
g = []
for i in range(n):
    lis = list(map(int, input().split()))
    g.append(lis)

dx = [1, 1]
dy = [1, -1]

def get_ans(x, y, d):
    res = 0
    h = {}
    while x >= 0 and y >= 0 and x < n and y < m:
        v = g[x][y]
        if v in h:
            res += h[v]
        else:
            h[v] = 0
        h[v] += 1
        
        x += dx[d]
        y += dy[d]
    
    return res

res = 0
for j in range(m):
    res += get_ans(0, j, 0)
for i in range(1, n):
    res += get_ans(i, 0, 0)

for j in range(m):
    res += get_ans(0, j, 1)
for i in range(1, n):
    res += get_ans(i, m - 1, 1)

print(res * 2)

神奇闹钟

问题描述

小蓝发现了一个神奇的闹钟,从纪元时间(19701970197011111100:00:0000:00:00000000)开始,每经过 xxx 分钟,这个闹钟便会触发一次闹铃 (纪元时间也会响铃)。这引起了小蓝的兴趣,他想要好好研究下这个闹钟。

对于给出的任意一个格式为 уууу-MM-ddHH:mm:ss\text{уууу-MM-dd\quad HH:mm:ss}уууу-MM-ddHH:mm:ss 的时间,小蓝想要知道在这个时间点之前 (包含这个时间点) 的最近的一次闹铃时间是哪个时间?

注意,你不必考虑时区问题。

输入格式

输入的第一行包含一个整数 TTT,表示每次输入包含 TTT 组数据。

接下来依次描述 TTT 组数据。

每组数据一行,包含一个时间(格式为 уууу-MM-ddHH:mm:ss\text{уууу-MM-dd\quad HH:mm:ss}уууу-MM-ddHH:mm:ss)和一个整数 xxx,其中 xxx 表示闹铃时间间隔(单位为分钟)。

输出格式

输出 TTT 行,每行包含一个时间(格式为 уууу-MM-ddHH:mm:ss\text{уууу-MM-dd\quad HH:mm:ss}уууу-MM-ddHH:mm:ss),依次表示每组数据的答案。

样例输入

2 2016-09-07 18:24:33 10 2037-01-05 01:40:43 30

样例输出

2016-09-07 18:20:00 2037-01-05 01:30:00

评测用例规模与约定

对于所有评测用例,1≤T≤10,1≤x≤10001 \leq T \leq {10},1 \leq x \leq {1000}1T10,1x1000,保证所有的时间格式都是合法的。

import sys
import datetime
input=lambda :sys.stdin.readline().strip()
start=datetime.datetime(1970,1,1,0,0,0)
for i in range(int(input())):
    a,b,c=input().split()
    y,m,d=map(int,a.split('-'))
    h,mi,s=map(int,b.split(':'))
    now=datetime.datetime(y,m,d,h,mi,0)
    diff=now-start
    totmins=diff.total_seconds()//60%int(c)
    print(str(now-datetime.timedelta(minutes=totmins)))

蓝桥村的真相

问题描述

在风景如画的蓝桥村,nnn 名村民围坐在一张古老的圆桌旁,参与一场思想的较量。这些村民,每一位都有着鲜明的身份:要么是誉满乡野的诚实者,要么是无可救药的说谎者。

当会议的钟声敲响,一场关于真理与谬误的辩论随之展开。每位村民轮流发言,编号为 iii 的村民提出了这样的断言:坐在他之后的两位村民 — 也就是编号 i+1i+1i+1i+2i+2i+2(注意,编号是环形的,所以如果 iii 是最后一个,则 i+1i+1i+1 是第一个,以此类推)之中,一个说的是真话,而另一个说的是假话。

在所有摇曳不定的陈述中,有多少真言隐藏在谎言的面纱之后?

请你探索每一种可能的真假排列组合,并计算在所有可能的真假组合中,说谎者的总数。

输入格式

第一行输入一个整数 TTT,表示数据的组数。

接下来 TTT 行,每行包含一个整数 nnn,表示村落的人数。

输出格式

对于每组数据,输出一行包含一个整数,表示答案。

样例输入

1 3

样例输出

6

样例说明

在样例中,可能的组合有 「假,假,假」「真,真,假」「真,假,真」「假,真,真」,说谎者的总数为 3+1+1+1=63 + 1 + 1 + 1 = 63+1+1+1=6

评测用例规模与约定

对于 10%10\%10% 的评测用例,T=1T = 1T=13≤n≤103\leq n \leq 103n10

对于 40%40\%40% 的评测用例,1≤T≤1021\leq T \leq 10^21T1023≤n≤3×1033\leq n \leq 3\times 10^33n3×103

对于所有评测用例,1≤T≤1051\leq T \leq 10^51T1053≤n≤10183\leq n \leq 10^{18}3n1018

解题

真–》真–》假 --》真–》真–》假

真–》假–》真 --》真–》假–》真

假–》真–》真 --》假–》真–》真

假–》假–》假 --》假–》假–》假

n=int(input())
for i in range(n):
  m=int(input())
  if m%3==0:
    print(2*m)
  else:
    print(m)

魔法巡游

问题描述

在蓝桥王国中,两位魔法使者,小蓝与小桥,肩负着维护时空秩序的使命。他们每人分别持有 NNN 个符文石,这些石头被赋予了强大的力量,每一块上都刻有一个介于 1 到 10910^9109 之间的数字符号。小蓝的符文石集合标记为 s1,s2,…,sNs_1, s_2, \ldots, s_Ns1,s2,,sN,小桥的则为 t1,t2,…,tNt_1, t_2, \ldots, t_Nt1,t2,,tN

两位魔法使者的任务是通过使用符文石,在各个时空节点间巡游。每次巡游遵循这样一条法则:当小蓝使用了符文石 sis_isi 到达新的节点后,小桥必须选用一个序号更大的符文石(即某个 tjt_jtj 满足 j>ij>ij>i)前往下一个节点。同理,小桥抵达之后,小蓝需要选择一个序号 j>ij>ij>i 的符文石 sjs_jsj 继续他们的巡游。

为了成功地穿梭时空,两个连续使用的符文石上的数字符号必须有共鸣,这种共鸣只有当数字符号中至少包含一个特定的元素——星火(数字 000)、水波(数字 222)或者风语(数字 444)时,才会发生。例如,符号序列 126,552,24,4126,552,24,4126,552,24,4 中的每对连续符文都包含了至少一个共鸣元素,则它们是一系列成功的巡游;而如果是 15,51,515,51,515,51,5,则不成立,因为它们之间的共鸣元素不包含星火、水波或风语中的任意一个。

小蓝总是先启程,使用他的符文石开启巡游。

你的任务是计算这对魔法使者能够执行的最长时空巡游序列的长度。这样的序列形式为 si1,ti2,si3,ti4,…s_{i_1}, t_{i_2}, s_{i_3}, t_{i_4}, \ldotssi1,ti2,si3,ti4,,其中序列索引满足 i1<i2<i3<i4<…i_1 < i_2 < i_3 < i_4 < \ldotsi1<i2<i3<i4<,并且序列中每一对相邻的符文石都至少包含一个共鸣元素。

输入格式

第一行包含一个整数 NNN,表示每位魔法使者持有的符文石数量。

第二行包含 NNN 个由空格分隔的整数,表示小蓝的符文石上刻有的数字符号 s1,s2,…,sNs_1, s_2, \ldots, s_Ns1,s2,,sN

第三行包含 NNN 个由空格分隔的整数,表示小桥的符文石上刻有的数字符号 t1,t2,…,tNt_1, t_2, \ldots, t_Nt1,t2,,tN

输出格式

输出一个整数,代表小蓝和小桥在遵守所有规则的情况下,最多能进行多少次时空巡游。

样例输入

5 126 393 581 42 44 204 990 240 46 52

【样例输出】

4

【样例说明】

小蓝和小桥可以选择以下符文石序列进行巡游: s1(126)→t3(240)→s4(42)→t5(52)s_1(126) \rightarrow t_3(240) \rightarrow s_4(42) \rightarrow t_5(52)s1(126)t3(240)s4(42)t5(52) 这里,数字 222 作为共鸣元素连接了 s1s_1s1t3t_3t3s4s_4s4t5t_5t5,数字 222444 作为共鸣元素连接了 t3t_3t3s4s_4s4

评测用例规模与约定

对于 3030%30 的评测用例,1≤N≤1031 \leq N \leq 10^31N1031≤si,ti≤1051\leq s_i, t_i \leq 10^51si,ti105

对于所有评测用例,1≤N≤1051\leq N \leq 10^51N1051≤si,ti≤1091\leq s_i, t_i \leq 10^91si,ti109

cin = lambda: list(map(int, input().split()))
 
input()
f1, f2 = [-1] * 5, [-1] * 5
digs = (0, 2, 4)
 
def update(s, f1, f2):
    for d in digs:
        if str(d) in s:
            f1[d] = max(f1[d], max(f2[i] + 1 for i in digs if str(i) in s))
 
for s, t in zip(cin(), cin()):
    s, t = str(s), str(t)
    f2_ = f2.copy() # 拷贝
    update(t, f2, f1)
    for d in digs:
        if str(d) in s and f1[d] < 0:
            f1[d] = max(*f1, 1) # 确保先从 s 开始走
    update(s, f1, f2_)
            
print(max(f1 + f2))

动态规划

import sys

sys.setrecursionlimit(1000000)
input = lambda: sys.stdin.readline().strip()


def get(x):
    a, b, c = False, False, False
    while x > 0:
        t = x % 10
        if t == 0:
            a = True
        elif t == 2:
            b = True
        elif t == 4:
            c = True
        x //= 10
    return [a, b, c]


n = int(input())
a = list(map(int, input().split()))
b = list(map(int, input().split()))

fa = [0 for i in range(n + 1)]
fb = [0 for i in range(n + 1)]

pa = [n, n, n]
pb = [n, n, n]
for i in range(n - 1, 0 - 1, -1):
    x = get(a[i])
    y = get(b[i])
    for j in range(3):
        if x[j]:
            fa[i] = max(fa[i], fb[pb[j]] + 1)
        if y[j]:
            fb[i] = max(fb[i], fa[pa[j]] + 1)

    for j in range(3):
        if x[j] and fa[i] > fa[pa[j]]:
            pa[j] = i
        if y[j] and fb[i] > fb[pb[j]]:
            pb[j] = i

print(max(fa))

二分

import bisect

bisect.bisect(ls,7)返回大于x的第一个下标

bisect.bisect_left(ls,7)返回大于等于x的第一个下标

最大满足

while(left<right):
  mid=(left+right+1)>>1
  if(s[x]<=goal):
    left=mid
  else:
    right=mid-1
print(left,s[left])

最小满足

while(left<right):
  mid=(left+right)>>1
  if(s[x]>=goal):
    right=mid
  else:
    left=mid+1
print(left,s[left])
#二分模板
lst=[1,2,3,4,5,5,5,6,7,8,9,10]
def binary_search1(lst,target):#往右找;最左边
    l=0
    r=len(lst)-1
    while l<r:
        mid = l + r >> 1
        if lst[mid] >= target:
            r = mid
        else:
            l = mid + 1
    return l,r
def binary_search2(lst,target):#往左找;最右边
    l=0
    r=len(lst)-1
    while l<r:
        mid = l + r +1 >> 1
        if lst[mid] <= target:
            l = mid
        else:
            r = mid - 1
    return l,r

print(binary_search1(lst,5))
print(binary_search2(lst,5))

缴纳过路费

问题描述

在繁华的商业王国中,NNN 座城市被 MMM 条商路巧妙地连接在一起,形成了一个错综复杂的无向图网络。每条商路是双向通行的,并且任意两座城市之间最多只有一条直接的商路。每条商路都有它的规则,其中最引人注目的就是穿过商路,需要缴纳过路费。因此,商人们在选择商路时必须格外认真。

有一位名叫小蓝的商人,他对于商路的花费有着自己独到的见解。在小蓝眼中,一条路线包含一条或多条商路,但路线的成本并不是沿途累积的过路费总和,而是这条路线上最贵的那一次收费。这个标准简单而直接,让他能迅速评估出一条路线是否划算。

于是,他设立了一个目标,即找出所有城市对,这些城市之间的最低路线成本介于他心中预设的两个数 LLLRRR 之间。他相信,这样的路线既不会太廉价,以至于路况糟糕;也不会过于昂贵,伤害他精打细算的荷包。

作为小蓝的助手,请你帮助小蓝统计出所有满足条件的城市对数量。

输入格式

第一行包含四个整数 N,M,L,RN,M,L,RN,M,L,R,表示有 NNN 座城市和 MMM 条双向通行的商路,以及小蓝心中预设的最高过路费的下限 LLL 和上限 RRR

接下来 MMM 行,每行包含三个整数 u,v,wu,v,wu,v,w,表示城市 uuu 和城市 vvv 之间有一条双向通行的商路,过路费为 www。保证每对城市之间最多只有一条直接的商路。

输出格式

输出仅一行,包含一个整数,表示满足条件的城市对数量。

样例输入

5 5 1 2 1 2 2 1 3 5 1 4 1 2 4 5 2 5 4

样例输出

3

样例说明

在样例中,满足条件的城市对有 (1,2)(1,2)(1,2)(1,4)(1,4)(1,4)(2,4)(2,4)(2,4)

评测用例规模与约定

对于 30%30\%30% 的评测用例,1≤N≤1031 \leq N \leq 10^31N1031≤M≤min⁡(2×103,N×(N−1)2)1\leq M \leq \min(2\times10^3 , \frac{N\times (N-1)}{2})1Mmin(2×103,2N×(N1))1≤L≤R≤1051\leq L\leq R \leq 10^51LR1051≤u,v≤N,u≠v1\leq u,v \leq N, u\neq v1u,vN,u=v1≤w≤1051\leq w \leq 10^51w105

对于所有评测用例,1≤N≤1051\leq N \leq 10^51N1051≤M≤min⁡(2×105,N×(N−1)2)1\leq M \leq \min(2\times 10^5, \frac{N\times (N-1)}{2})1Mmin(2×105,2N×(N1))1≤L≤R≤1091\leq L\leq R \leq 10^91LR109,1≤u,v≤N,u≠v1\leq u,v \leq N, u\neq v1u,vN,u=v1≤w≤1091\leq w \leq 10^91w109

from sys import *
 
setrecursionlimit(10**6)
cin = lambda: list(map(int, input().split()))
 
n, m, l, r = cin()
e = [set() for _ in range(n + 1)]
# s[i] == i 表示 i 为新点,sz[i] 表示新点 i 的大小
s, sz = list(range(n + 1)), [1] * (n + 1)
 
def find(x):
    if s[x] == x:
        return x
    s[x] = find(s[x])
    return s[x]
 
edges = [cin() for _ in range(m)]
for u, v, w in edges:
    if w > r: continue # 舍弃
    su, sv = find(u), find(v)
    if w < l and su - sv: # 边权小于 l 且不在同一集合
        # 合并
        s[su] = sv
        sz[sv] += sz[su]
# 以新点建图
for u, v, w in edges:
    if l <= w <= r:
        su, sv = find(u), find(v)
        e[su].add(sv)
        e[sv].add(su)
 
vis = [False] * (n + 1)
 
def dfs(u, block): # 寻找连通块,返回值表示该块内点的集合的总大小
    block.append(u)
    res, vis[u] = sz[u], True
    for v in e[u]:
        if vis[v]: continue
        res += dfs(v, block)
    return res
 
ans = 0
for i in range(1, n + 1):
    if s[i] == i and not vis[i]:
        block = []
        t = dfs(i, block)
 
        for u in block:
            ans += sz[u] * (t - sz[u])
 
print(ans // 2)

问题描述

在蓝桥王国,国王统治着一支由 nnn 个小队组成的强大军队。每个小队都由相同职业的士兵组成。具体地,第 iii 个小队包含了 bib_ibi 名职业为 aia_iai 的士兵。

近日,国王计划在王宫广场举行一场盛大的士兵检阅仪式,以庆祝王国的繁荣昌盛。然而,在士兵们入场的过程中,一场突如其来的风暴打乱了他们的行列,使得不同小队的士兵混杂在一起,次序乱成一团,

尽管国王无法知道每个士兵的具体职业,但为了确保仪式能顺利进行,国王打算从这些混乱的士兵中选出一部分,组成 kkk 个“纯职业小组”进行检阅。一个“纯职业小组”定义为由 333 名同职业的士兵组成的队伍。

请问,国王至少需要选择多少名士兵,才能确保这些士兵可以组成 kkk 个“纯职业小组”。

输入格式

输入包含多组数据。

第一行包含一个整数 TTT,表示有 TTT 组数据。

对于每组数据:

  • 第一行包含两个整数 ntn_tntkkk,表示小队的数量和要组成的纯职业小组的数量。
  • 接下来的 ntn_tnt 行,每行包含两个整数 aia_iaibib_ibi,表示第 iii 个小队中士兵的职业和数量。

输出格式

对于每组数据,输出一个整数,表示为了组成 kkk 个“纯职业小组”,国王至少需要选择的士兵数量。如果无论如何也无法组成 kkk 个“纯职业小组”,则输出 −1-11

样例输入

2
3 2
1 3
2 3
3 3
3 5
1 3
2 3
3 3

样例输出

8
-1

样例说明

在第一个样例中,要想组成 222 个“纯职业小组”,国王至少需要选择 888 名士兵。若只选择了 777 名士兵,则这 777 名士兵的职业可能为 1,1,1,2,2,3,31, 1, 1, 2 , 2 , 3 ,31,1,1,2,2,3,3,无法组成 222 个“纯职业小组”。

在第二个样例中,即使选择了所有士兵,也无法组成 555 个“纯职业小组”,因此输出 −1-11

评测用例规模与约定

对于 50%50\%50% 的评测用例,1≤T≤101\leq T\leq 101T101≤∑t=1Tnt≤2×1031\leq \sum \limits_{t=1}^T n_t \leq 2\times 10^31t=1Tnt2×1031≤ai,bi≤1051\leq a_i, b_i \leq 10^51ai,bi1051≤k≤1071\leq k \leq 10^71k107

对于所有的评测用例,1≤T≤1001\leq T \leq 1001T1001≤∑t=1Tnt≤2×1051\leq \sum\limits_{t=1}^T n_t \leq 2\times 10^51t=1Tnt2×1051≤ai,bi≤1091\leq a_i, b_i \leq 10^91ai,bi1091≤k≤10131\leq k \leq 10^{13}1k1013

解题思路

这道题我的思路比较抽象,大概意思就是找一个数,能恰好组成 k 组(不冗余),且这个数尽可能大。

显然只有当每一组都选完还组不成 k 组的时候输出 -1,否则一定能组成 k 组。

如果能组成 k 组,那么设一个计数器 m = k 表示还缺几组没有凑够。

先在同职业中选 1 ~ 2 个人,这不会增加组数,然后再看剩下的人数是 3 的几倍,如果这个倍数小于 m 的话就都加上。

当遍历完后,这时候 m 仍然小于 k,再加上 m * 3 - 2 就是正确答案。为什么要 -2 呢?因为最后缺的一组再加 1 个人就凑够了。

下面的代码直接用 k 来代替 m 了

from collections import *
cin = lambda: list(map(int, input().split()))
 
for _ in range(int(input())):
    n, k = cin()
    cnt = Counter()
    for i in range(n):
        a, b = cin()
        cnt[a] += b
    if sum(c // 3 for c in cnt.values()) < k:
        print(-1)
        continue
    ans = 0
    for a, b in cnt.items():
        ans += min(b, 2) # 1 或 2
        b -= min(b, 2) # 这行不加,答案也是正确的
        if b // 3 < k:
            ans += b // 3 * 3
            k -= b // 3
    print(ans + k * 3 - 2)

Counter

from collections import Counter

list1 = ["a", "a", "a", "b", "c", "c", "f", "g", "g", "g", "f"]
dic = Counter(list1)
print(dic)
#结果:次数是从高到低的
#Counter({'a': 3, 'g': 3, 'c': 2, 'f': 2, 'b': 1})

str1 = "aabbfkrigbgsejaae"
print(Counter(str1))
print(dict(Counter(str1)))
#结果:
#Counter({'a': 4, 'b': 3, 'g': 2, 'e': 2, 'f': 1, 'k': 1, 'r': 1, 'i': 1, 's': 1, 'j': 1})
#{'a': 4, 'b': 3, 'f': 1, 'k': 1, 'r': 1, 'i': 1, 'g': 2, 's': 1, 'e': 2, 'j': 1}

list1 = ["a", "a", "a", "b", "c", "f", "g", "g", "c", "11", "g", "f", "10", "2"]
print(Counter(list1).most_common(3))
#结果:[('a', 3), ('g', 3), ('c', 2)]

#Counter相加减
a.update(b)
a.subtract(b)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值