题目1:连号区间数
[蓝桥杯 2013 省 B] 连号区间数
题目描述
小明这些天一直在思考这样一个奇怪而有趣的问题:
在 1 1 1 ~ N N N 的某个全排列中有多少个连号区间呢?这里所说的连号区间的定义是:
如果区间 [ L , R ] [L, R] [L,R] 里的所有元素(即此排列的第 L L L个到第 R R R 个元素)递增排序后能得到一个长度为 R − L + 1 R-L+1 R−L+1 的“连续”数列,则称这个区间连号区间。
当 N N N 很小的时候,小明可以很快地算出答案,但是当 N N N 变大的时候,问题就不是那么简单了,现在小明需要你的帮助。
输入格式
第一行是一个正整数 N ( 1 ≤ N ≤ 50000 ) N (1 \le N \le 50000) N(1≤N≤50000), 表示全排列的规模。
第二行是 N N N 个不同的数字 P i ( 1 ≤ P i ≤ N ) P_i(1 \le P_i \le N) Pi(1≤Pi≤N), 表示这 N N N 个数字的某一全排列。
输出格式
输出一个整数,表示不同连号区间的数目。
输入输出样例
输入
4
3 2 4 1
输出
7
输入输出样例 #2
输入 #2
5
3 4 2 5 1
输出 #2
9
说明/提示
第一个用例中,有 7 7 7 个连号区间分别是: [ 1 , 1 ] [1,1] [1,1], [ 1 , 2 ] [1,2] [1,2], [ 1 , 3 ] [1,3] [1,3], [ 1 , 4 ] [1,4] [1,4], [ 2 , 2 ] [2,2] [2,2], [ 3 , 3 ] [3,3] [3,3], [ 4 , 4 ] [4,4] [4,4]。
第二个用例中,有 9 9 9 个连号区间分别是: [ 1 , 1 ] [1,1] [1,1], [ 1 , 2 ] [1,2] [1,2], [ 1 , 3 ] [1,3] [1,3], [ 1 , 4 ] [1,4] [1,4], [ 1 , 5 ] [1,5] [1,5], [ 2 , 2 ] [2,2] [2,2], [ 3 , 3 ] [3,3] [3,3], [ 4 , 4 ] [4,4] [4,4], [ 5 , 5 ] [5,5] [5,5]。
原题时限 5 秒, 64M。蓝桥杯 2013 年第四届省赛
python代码
n=int(input())
data=list(map(int,input().split()))
ans=0
for i in range(n):
max1=float('-inf')
min1=float('inf')
for j in range(i,n,1):
max1=max(max1,data[j])
min1=min(min1,data[j])
if max1-min1==(j-i):
ans+=1
print(ans)
知识点
- 全排列,不涉及到重复元素的问题
- 那么找到区间内的最大值与最小值,如果最大值-最小值==r-l,符合条件
float('inf')
:正无穷大;float('-inf')
:负无穷大
题目2:递增三元组
[蓝桥杯 2018 省 B] 递增三元组
题目描述
给定三个整数数组 A = [ A 1 , A 2 , ⋯ , A N ] A = [A_1, A_2,\cdots, A_N] A=[A1,A2,⋯,AN], B = [ B 1 , B 2 , ⋯ , B N ] B = [B_1, B_2,\cdots, B_N] B=[B1,B2,⋯,BN], C = [ C 1 , C 2 , ⋯ , C N ] C = [C_1, C_2,\cdots,C_N] C=[C1,C2,⋯,CN]。
请你统计有多少个三元组 ( i , j , k ) (i, j, k) (i,j,k) 满足:
- 1 ≤ i , j , k ≤ N 1 \le i, j, k \le N 1≤i,j,k≤N
- A i < B j < C k A_i < B_j < C_k Ai<Bj<Ck
输入格式
第一行包含一个整数 N N N。
第二行包含 N N N 个整数 A 1 , A 2 , ⋯ , A N A_1, A_2,\cdots, A_N A1,A2,⋯,AN。
第三行包含 N N N 个整数 B 1 , B 2 , ⋯ , B N B_1, B_2,\cdots, B_N B1,B2,⋯,BN。
第四行包含 N N N 个整数 C 1 , C 2 , ⋯ , C N C_1, C_2,\cdots, C_N C1,C2,⋯,CN。
输出格式
一个整数表示答案
输入输出样例
输入
3
1 1 1
2 2 2
3 3 3
输出
27
说明/提示
对于 30 % 30\% 30% 的数据, 1 ≤ N ≤ 100 1 \le N \le 100 1≤N≤100。
对于 60 % 60\% 60% 的数据, 1 ≤ N ≤ 1000 1 \le N \le 1000 1≤N≤1000。
对于 100 % 100\% 100% 的数据, 1 ≤ N ≤ 1 0 5 1 \le N \le 10^5 1≤N≤105, 0 ≤ A i , B i , C i ≤ 1 0 5 0 \le A_i, B_i, C_i \le 10^5 0≤Ai,Bi,Ci≤105。
python代码
n=int(input())
N=10**5+10
A=list(map(int,input().split()))
B=list(map(int,input().split()))
C=list(map(int,input().split()))
#每一个元素都加一,方便前缀和下标从1开始,且不影响前后大小关系
A=[i+1 for i in A]
B=[i+1 for i in B]
C=[i+1 for i in C]
ass=[0]*N
css=[0]*N
#计算A
cnt1=[0]*N#某一个值出现的次数
S1=[0]*N
for i in range(n):
cnt1[A[i]]+=1
for i in range(1,N):#前缀和
S1[i]=S1[i-1]+cnt1[i]
for i in range(n):
ass[i]=S1[B[i]-1]
#计算C
cnt2=[0]*N#某一个值出现的次数
S2=[0]*N
for i in range(n):
cnt2[C[i]]+=1
for i in range(1,N):#前缀和
S2[i]=S2[i-1]+cnt2[i]
for i in range(n):
css[i]=S2[N-1]-S2[B[i]]
ans=0
for i in range(n):
ans+=(ass[i]*css[i])
# print(cnt1[0:10])
# print(S1[0:10])
# print(ass[0:5])
# print(css[0:5])
print(ans)
知识点
- 首先根据数据范围 1 0 5 10^5 105可知,最多使用一个循环,那么循环哪一组呢?A与C地位是等价的,假设遍历A中元素,那么只能做到 B j > A i , C j > A i B_j>A_i,C_j>A_i Bj>Ai,Cj>Ai,依然无法确定唯一的顺序,故只能遍历B中元素
- 下一步是利用什么方法?可以用前缀和 or 排序+二分方法,以前缀和为例
- 对于A列表,先开一个N长度的列表cnt1, c n t 1 [ A [ i ] ] cnt1[A[i]] cnt1[A[i]]统计A中每一个元素出现的次数,那么S1前缀和的含义是? S 1 [ A i ] 含义就是小于等于 A i 的个数 S1[A_i]含义就是小于等于A_i的个数 S1[Ai]含义就是小于等于Ai的个数
- 同样地,对于C列表,大于 B j B_j Bj的个数就是 S 2 [ N − 1 ] − S 2 [ B j ] S2[N-1]-S2[B_j] S2[N−1]−S2[Bj]
- 那么最后的方案数目就是
ass[i]*css[i]
的累加
题目3:封闭图形个数
[蓝桥杯 2024 省 C] 封闭图形个数
题目描述
在蓝桥王国,数字的大小不仅仅取决于它们的数值大小,还取决于它们所形成的“封闭图形”的个数。
封闭图形是指数字中完全封闭的空间,例如数字 1 1 1、 2 2 2、 3 3 3、 5 5 5、 7 7 7 都没有形成封闭图形,而数字 0 0 0、 4 4 4、 6 6 6、 9 9 9 分别形成了 1 1 1 个封闭图形,数字 8 8 8 则形成了 2 2 2 个封闭图形。值得注意的是,封闭图形的个数是可以累加的。例如,对于数字 68 68 68,由于 6 6 6 形成了 1 1 1 个封闭图形,而 8 8 8 形成了 2 2 2 个,所以 68 68 68 形成的封闭图形的个数总共为 3 3 3。
在比较两个数的大小时,如果它们的封闭图形个数不同,那么封闭图形个数较多的数更大。例如,数字 41 41 41 和数字 18 18 18,它们对应的封闭图形的个数分别为 1 1 1 和 2 2 2,因此数字 41 41 41 小于数组 18 18 18。如果两个数的封闭图形个数相同,那么数值较大的数更大。例如,数字 14 14 14 和数字 41 41 41,它们的封闭图形的个数都是 1 1 1,但 14 < 41 14 < 41 14<41,所以数字 14 14 14 小于数字 41 41 41。如果两个数字的封闭图形个数和数值都相同,那么这两个数字被认为是相等的。
小蓝对蓝桥王国的数字大小规则十分感兴趣。现在,他将给定你 n n n 个数 a 1 , a 2 , ⋯ , a n a_1, a_2,\cdots, a_n a1,a2,⋯,an,请你按照蓝桥王国的数字大小规则,将这 n n n 数从小到大排序,并输出排序后结果。
输入格式
输入的第一行包含一个整数 n n n,表示给定的数字个数。
第二行包含 n n n 个整数 a 1 , a 2 , ⋯ , a n a_1, a_2,\cdots, a_n a1,a2,⋯,an,相邻整数之间使用一个空格分隔,表示待排序的数字。
输出格式
输出一行包含 n n n 个整数,相邻整数之间使用一个空格分隔,表示按照蓝桥王国的数字大小规则从小到大排序后的结果。
输入输出样例 #1
输入 #1
3
18 29 6
输出 #1
6 29 18
说明/提示
【样例说明】
对于给定的数字序列 [ 18 , 29 , 6 ] [18, 29, 6] [18,29,6],数字 18 18 18 的封闭图形个数为 2 2 2,数字 29 29 29 的封闭图形个数为 1 1 1,数字 6 6 6 的封闭图形个数为 1 1 1。按照封闭图形个数从小到大排序后,得到 [ 29 , 6 , 18 ] [29, 6, 18] [29,6,18]。
由于数字 29 29 29 和数字 6 6 6 的封闭图形个数相同,因此需要进一步按照数值大小对它们进行排序,最终得到 [ 6 , 29 , 18 ] [6, 29, 18] [6,29,18]。
【评测用例规模与约定】
对于
50
%
50\%
50% 的评测用例,
1
≤
n
≤
2
×
1
0
3
1\le n \le 2 \times 10^3
1≤n≤2×103,
1
≤
a
i
≤
1
0
5
1 \le a_i \le 10^5
1≤ai≤105。
对于所有评测用例,
1
≤
n
≤
2
×
1
0
5
1 \le n\le 2 \times 10^5
1≤n≤2×105,
1
≤
a
i
≤
1
0
9
1 \le a_i \le 10^9
1≤ai≤109。
python代码
n=int(input())
data=list(map(int,input().split()))
lq=[0]*n
for i in range(n):
a=str(data[i])
sum1=sum2=0#统计数字
sum1=a.count('0')+a.count('4')+a.count('6')+a.count('9')
sum2=a.count('8')*2
lq[i]=sum1+sum2
#首先按照新的规则,计算每一个数字的“大小”,记为lq[i],然后用元组(data[i],lq[i])记录所有数据
combined_data=[(data[i],lq[i])for i in range(n)]
combined_data.sort(key=lambda x:(x[1],x[0]))
answer=[]
for i in range(n):
answer.append(combined_data[i][0])
print(*answer)
知识点
- 主要考察排序sort函数
- 首先按照新的规则,计算每一个数字的“大小”,记为lq[i],然后用元组(data[i],lq[i])记录所有数据
- 排序,
sort(*, key=None, reverse=False)
,只接受两个参数,在这里会先按照新规则排序,如果两个元素的新规则大小一样,则按照原数值排序 - 之后提取数据,并输出,
print(*answer)==print(*answer,sep=' ')
:默认按照空格 连接元素
题目4: 回文日期
[NOIP 2016 普及组] 回文日期
题目背景
NOIP2016 普及组 T2
题目描述
在日常生活中,通过年、月、日这三个要素可以表示出一个唯一确定的日期。
牛牛习惯用 8 8 8 位数字表示一个日期,其中,前 4 4 4 位代表年份,接下来 2 2 2 位代表月份,最后 2 2 2 位代表日期。显然:一个日期只有一种表示方法,而两个不同的日期的表 示方法不会相同。
牛牛认为,一个日期是回文的,当且仅当表示这个日期的 8 8 8 位数字是回文的。现在,牛牛想知道:在他指定的两个日期之间包含这两个日期本身),有多少个真实存在的日期是回文的。
一个 8 8 8 位数字是回文的,当且仅当对于所有的 i i i( 1 ≤ i ≤ 8 1 \le i \le 8 1≤i≤8)从左向右数的第 i i i 个数字和第 9 − i 9-i 9−i 个数字(即从右向左数的第 i i i 个数字)是相同的。
例如:
- 对于 2016 年 11 月 19 日,用 8 8 8 位数字 20161119 20161119 20161119 表示,它不是回文的。
- 对于 2010 年 1 月 2 日,用 8 8 8 位数字 20100102 20100102 20100102 表示,它是回文的。
- 对于 2010 年 10 月 2 日,用 8 8 8 位数字 20101002 20101002 20101002 表示,它不是回文的。
每一年中都有 12 12 12 个月份:
其中, 1 , 3 , 5 , 7 , 8 , 10 , 12 1, 3, 5, 7, 8, 10, 12 1,3,5,7,8,10,12 月每个月有 31 31 31 天; 4 , 6 , 9 , 11 4, 6, 9, 11 4,6,9,11 月每个月有 30 30 30 天;而对于 2 2 2 月,闰年时有 29 29 29 天,平年时有 28 28 28 天。
一个年份是闰年当且仅当它满足下列两种情况其中的一种:
- 这个年份是 4 4 4 的整数倍,但不是 100 100 100 的整数倍;
- 这个年份是 400 400 400 的整数倍。
例如:
- 以下几个年份都是闰年: 2000 , 2012 , 2016 2000, 2012, 2016 2000,2012,2016。
- 以下几个年份是平年: 1900 , 2011 , 2014 1900, 2011, 2014 1900,2011,2014。
输入格式
两行,每行包括一个 8 8 8 位数字。
第一行表示牛牛指定的起始日期。
第二行表示牛牛指定的终止日期。
保证 d a t e 1 \mathit{date}_1 date1 和 d a t e 2 \mathit{date}_2 date2 都是真实存在的日期,且年份部分一定为 4 4 4 位数字,且首位数字不为 0 0 0。
保证 d a t e 1 \mathit{date}_1 date1 一定不晚于 d a t e 2 \mathit{date}_2 date2。
输出格式
一个整数,表示在 d a t e 1 \mathit{date}_1 date1 和 d a t e 2 \mathit{date}_2 date2 之间,有多少个日期是回文的。
输入输出样例 #1
输入 #1
20110101
20111231
输出 #1
1
输入输出样例 #2
输入 #2
20000101
20101231
输出 #2
2
说明/提示
【样例说明】
对于样例 1,符合条件的日期是 20111102 20111102 20111102。
对于样例 2,符合条件的日期是 20011002 20011002 20011002 和 20100102 20100102 20100102。
【子任务】
对于 60 % 60 \% 60% 的数据,满足 d a t e 1 = d a t e 2 \mathit{date}_1 = \mathit{date}_2 date1=date2。
python代码
start=input()
end=input()
a=int(start)
b=int(end)
ans=0
days=[0,31,28,31,30,31,30,31,31,30,31,30,31]
def check(date):
year=date//10000
month=date%10000//100
day=date%100
if (year%4==0 and year%100!=0) or year%400==0:#判断是否是闰年
leap=1
else:
leap=0
if month==0 or month >12:return False
if day==0 or (month!=2 and day>days[month]):return False
if month==2:
if day >28+leap:return False
return True
for i in range(1000,10000):#逆向思维,先枚举回文数,前四位,然后判断是否在范围内,再判断日期是否合法
date=i
x=i
for j in range(4):
date=date*10+x%10
x//=10
if date>=a and date<=b and check(date):
ans+=1
print(ans)
知识点
- 正向的思维是,遍历日期,然后判断是否是回文数组,但是 1 0 8 10^8 108会超时
- 逆向思维是,遍历前四位数,这时后四位数已经确定该回文数,然后判断:
- 是否在 [ d a t e 1 , d a t e 2 ] [date_1,date_2] [date1,date2]
- 是否是合法日期 - 抠出来date的每一位,
while (date): x=date%10 date//=10 print(x)
- 判断闰年,
year%4==0 and year%4!=0 or year%400==0
- 更多细节知识点,
蓝桥杯python组备赛笔记(超详细版)
题目5:移动距离
X星球居民小区的楼房全是一样的,并且按矩阵样式排列。
其楼房的编号为 1,2,3…
当排满一行时,从下一行相邻的楼往反方向排号。
比如:当小区排号宽度为 6 时,开始情形如下:
1 2 3 4 5 6
12 11 10 9 8 7
13 14 15 .....
我们的问题是:已知了两个楼号 m 和 n,需要求出它们之间的最短移动距离(不能斜线方向移动)。
输入格式
输入共一行,包含三个整数 w,m,n,w 为排号宽度,m,n 为待计算的楼号。
输出格式
输出一个整数,表示 m,n 两楼间最短移动距离。
数据范围
1≤w,m,n≤10000,
输入样例:
6 8 2
输出样例:
4
python代码
w,m,n=map(int,input().split())
m-=1
n-=1
ans=0
a,b=divmod(m,w)
c,d=divmod(n,w)
#第一个位置坐标为(0,0)
if a%2!=0:#奇数行需要更改顺序
b=w-b-1
if c%2!=0:#奇数行需要更改顺序
d=w-d-1
ans=abs(c-a)+abs(d-b)
print(ans)
知识点
- 根据元素在矩阵的坐标来得到答案,第一个位置坐标为(0,0)
- 偶数行是正常的顺序,只有奇数行逆序,因此只需要对奇数行进行处理
题目6:日期问题
小明正在整理一批历史文献。这些历史文献中出现了很多日期。
小明知道这些日期都在1960年1月1日至2059年12月31日。
令小明头疼的是,这些日期采用的格式非常不统一,有采用年/月/日的,有采用月/日/年的,还有采用日/月/年的。
更加麻烦的是,年份也都省略了前两位,使得文献上的一个日期,存在很多可能的日期与其对应。
比如02/03/04,可能是2002年03月04日、2004年02月03日或2004年03月02日。
给出一个文献上的日期,你能帮助小明判断有哪些可能的日期对其对应吗?
输入格式
一个日期,格式是”AA/BB/CC”。
即每个’/’隔开的部分由两个 0-9 之间的数字(不一定相同)组成。
输出格式
输出若干个不相同的日期,每个日期一行,格式是”yyyy-MM-dd”。
多个日期按从早到晚排列。
数据范围
0≤A,B,C≤9
输入样例:
02/03/04
输出样例:
2002-03-04
2004-02-03
2004-03-02
python代码
def check(year,month,day):
days=[0,31,28,31,30,31,30,31,31,30,31,30,31]
if month>12 or month==0:
return False
if day ==0 or (month!=2 and day>days[month]):return False
if (year%4==0 and year%100!=0) or year%400==0:
leap=1
else:
leap=0
if month==2:
if day>28+leap:return False
return True
if __name__=="__main__":
a,b,c=map(int,input().split('/'))
for i in range(19600101,20591231):
s=str(i)
year=int(s[2:4])
month=int(s[4:6])
day=int(s[6:])
if check(year,month,day):#判断日期是否合法
if (year==a and month==b and day== c) or (month==a and day==b and year== c) or (day==a and month==b and year== c):
ans=s[:4]+'-'+s[4:6]+'-'+s[6:]
print(ans)
知识点
- 一共只有100年,100*365,数据规模支持遍历每一天
- 正向思维是,排列组合00~99,然后判断日期是否合法,最后需要进行排序
- 逆向思维是,遍历数字,然后截取年/月/日的,或月/日/年的,或日/月/年,是否与输入 相等,之后判断日期是否合法
- 主要考察 日期是否合法
题目7:航班时间
小 h 前往美国参加了蓝桥杯国际赛。
小 h 的女朋友发现小 h 上午十点出发,上午十二点到达美国,于是感叹到“现在飞机飞得真快,两小时就能到美国了”。
小 h 对超音速飞行感到十分恐惧。
仔细观察后发现飞机的起降时间都是当地时间。
由于北京和美国东部有 12 小时时差,故飞机总共需要 14 小时的飞行时间。
不久后小 h 的女朋友去中东交换。
小 h 并不知道中东与北京的时差。
但是小 h 得到了女朋友来回航班的起降时间。
小 h 想知道女朋友的航班飞行时间是多少。
对于一个可能跨时区的航班,给定来回程的起降时间。
假设飞机来回飞行时间相同,求飞机的飞行时间。
输入格式
一个输入包含多组数据。
输入第一行为一个正整数 T,表示输入数据组数。
每组数据包含两行,第一行为去程的起降时间,第二行为回程的起降时间。
起降时间的格式如下:
- h1:m1:s1 h2:m2:s2
- h1:m1:s1 h3:m3:s3 (+1)
- h1:m1:s1 h4:m4:s4 (+2)
第一种格式表示该航班在当地时间h1时m1分s1秒起飞,在当地时间当日h2时m2分s2秒降落。
第二种格式表示该航班在当地时间h1时m1分s1秒起飞,在当地时间次日h2时m2分s2秒降落。
第三种格式表示该航班在当地时间h1时m1分s1秒起飞,在当地时间第三日h2时m2分s2秒降落。
输出格式
对于每一组数据输出一行一个时间hh:mm:ss,表示飞行时间为hh小时mm分ss秒。
注意,当时间为一位数时,要补齐前导零,如三小时四分五秒应写为03:04:05。
数据范围
保证输入时间合法(0≤h≤23,0≤m,s≤59),飞行时间不超过24小时。
输入样例:
3
17:48:19 21:57:24
11:05:18 15:14:23
17:21:07 00:31:46 (+1)
23:02:41 16:13:20 (+1)
10:19:19 20:41:24
22:19:04 16:41:09 (+1)
输出样例:
04:09:05
12:10:39
14:22:05
python代码
#字符串处理
n=int(input())
for i in range(n):
a=input()
if a[-1]!=')':#没有 到第二天
h1=int(a[0:2])
m1=int(a[3:5])
s1=int(a[6:8])
seconds1=h1*3600+m1*60+s1
h2=int(a[9:11])
m2=int(a[12:14])
s2=int(a[15:16])
seconds2=h2*3600+m2*60+s2
else:#存在第二天,甚至到了第三天
h1=int(a[0:2])
m1=int(a[3:5])
s1=int(a[6:8])
seconds1=h1*3600+m1*60+s1
h2=int(a[9:11])
m2=int(a[12:14])
s2=int(a[15:16])
seconds2=h2*3600+m2*60+s2
day=int(a[20])
seconds2+=day*24*3600#最终的秒数
b=input()
if b[-1]!=')':#没有 到第二天
h3=int(b[0:2])
m3=int(b[3:5])
s3=int(b[6:8])
seconds3=h3*3600+m3*60+s3
h4=int(b[9:11])
m4=int(b[12:14])
s4=int(b[15:16])
seconds4=h4*3600+m4*60+s4
else:#存在第二天,甚至到了第三天
h3=int(b[0:2])
m3=int(b[3:5])
s1=int(b[6:8])
seconds3=h3*3600+m3*60+s3
h2=int(b[9:11])
m2=int(b[12:14])
s2=int(b[15:16])
seconds4=h4*3600+m4*60+s4
day2=int(b[20])
seconds4+=day2*24*3600#最终的秒数
ans=((seconds2-seconds1)+(seconds4-seconds3))//2
#格式化处理日期
hour=ans//3600
minute=ans//60-hour*60
seconds=ans%60
print(f'{hour:>02d}:{minute:>02d}:{seconds:>02d}')
知识点
- 主要考察字符串的处理,把时间都化成秒来计算
- 飞行时间=((到达时间-出发时间)+时差+(到达时间-出发时间)-时差)//2
- 当最后存在(+1)或(+2),把相应的秒数加上
- 格式化输出日期,
>表示右对齐,左面不足的补0,'2'表示宽度,d表示十进制整数
题目8:外卖店优先级
“饱了么”外卖系统中维护着 N 家外卖店,编号 1∼N。
每家外卖店都有一个优先级,初始时 (0 时刻) 优先级都为 0。
每经过 1 个时间单位,如果外卖店没有订单,则优先级会减少 1,最低减到 0;而如果外卖店有订单,则优先级不减反加,每有一单优先级加 2。
如果某家外卖店某时刻优先级大于 5,则会被系统加入优先缓存中;如果优先级小于等于 3,则会被清除出优先缓存。
给定 T 时刻以内的 M 条订单信息,请你计算 T 时刻时有多少外卖店在优先缓存中。
输入格式
第一行包含 3 个整数 N,M,T。
以下 M 行每行包含两个整数 ts 和 id,表示 ts 时刻编号 id 的外卖店收到一个订单。
输出格式
输出一个整数代表答案。
数据范围
1≤N,M,T≤105,
1≤ts≤T,
1≤id≤N
输入样例:
2 6 6
1 1
5 2
3 1
6 2
2 1
6 2
输出样例:
1
样例解释
6 时刻时,1 号店优先级降到 3,被移除出优先缓存;2 号店优先级升到 6,加入优先缓存。
所以是有 1 家店 (2 号) 在优先缓存中。
思路
- 按照id 排序
- id相同,则按照时间排序
- 计算有订单的最后一个时刻time1,保存下来,以及它的id
- 合并没有订单的区间,即处理time1之前的数据
- 处理time1时刻数据,优先级+2*cnt
- 如果time1<t,再处理最后一段 没有订单的区间
python代码
n,m,t=map(int,input().split())
data=[]
for i in range(m):#输入数据
a,b=map(int,input().split())
data.append((a,b))
data.sort(key=lambda x:(x[1],x[0]))
score=[0]*(n+1)#第i个店铺的优先级,从1开始
last=[0]*(n+1)#第i个店铺上一次有订单的时刻
st=[False]*(n+1)#是否处于优先缓冲区,True表示处于
# print(data)
i=0
while i<m:#一次处理一批数据,id和时刻都相同
j=i
while j>=0 and j<m and (data[j][0]==data[i][0]):#处理同一时刻,同一id有订单的区间
j+=1
# print(j)
time1=data[i][0]
id=data[i][1]
cnt=j-i
# print(time1,id,cnt)
score[id]-=(time1-last[id]-1)#没有订单
if score[id]<0:
score[id]=0
if score[id]<=3:#移出来 优先缓冲区
st[id]=False
score[id]+=cnt*2#处理有订单的这一时刻
if score[id]>5:#优先缓冲区
st[id]=True
i=j
last[id]=time1
for i in range(1,n+1):
if last[i]<t:#处理最后的一段,没有订单的
score[i]-=t-last[i]#没有订单
if score[i]<=3:#移出来 优先缓冲区
st[i]=False
ans=0
for i in range(1,n+1):
if st[i]==True:
ans+=1
print(ans)
知识点
data.sort(key=lambda x:(x[1],x[0]))
:对data数据进行排序,先按照每个元素的第2个值,即店铺的id排序,若id相同,按照元素的第一个值即时间排序
更多细节知识点,
蓝桥杯python组备赛笔记(超详细版)