title: 2024牛客寒假训练营1赛后补题总结(python实现)
date: 2024-02-03 12:27:41
mathjax: true
tags: [XCPC, 牛客竞赛]
categories: Algorithm
2024牛客寒假训练营1赛后补题总结(python实现)
文章目录
- 2024牛客寒假训练营1赛后补题总结(python实现)
- 前言
- [A-DFS搜索 (nowcoder.com)](https://ac.nowcoder.com/acm/contest/67741/A)
- [M-牛客老粉才知道的秘密 (nowcoder.com)](https://ac.nowcoder.com/acm/contest/67741/M)
- [E-本题又主要考察了贪心 (nowcoder.com)](https://ac.nowcoder.com/acm/contest/67741/E)
- [B-关鸡_2024牛客寒假算法基础集训营1 (nowcoder.com)](https://ac.nowcoder.com/acm/contest/67741/B)
- [C-按闹分配_2024牛客寒假算法基础集训营1 (nowcoder.com)](https://ac.nowcoder.com/acm/contest/67741/C)
- [G-why买外卖_2024牛客寒假算法基础集训营1 (nowcoder.com)](https://ac.nowcoder.com/acm/contest/67741/G)
- [L-要有光_2024牛客寒假算法基础集训营1 (nowcoder.com)](https://ac.nowcoder.com/acm/contest/67741/L)
- [I-It's bertrand paradox. Again!_2024牛客寒假算法基础集训营1 (nowcoder.com)](https://ac.nowcoder.com/acm/contest/67741/I)
- [F-鸡数题!_2024牛客寒假算法基础集训营1 (nowcoder.com)](https://ac.nowcoder.com/acm/contest/67741/F)
- [H-01背包,但是bit_2024牛客寒假算法基础集训营1 (nowcoder.com)](https://ac.nowcoder.com/acm/contest/67741/H)
- [D-数组成鸡_2024牛客寒假算法基础集训营1 (nowcoder.com)](https://ac.nowcoder.com/acm/contest/67741/D)
- [K-牛镇公务员考试_2024牛客寒假算法基础集训营1 (nowcoder.com)](https://ac.nowcoder.com/acm/contest/67741/K)
前言
-
I , L I,L I,L难度比较有趣,真实难度可能差不多,但因为都是榜单会影响难度的题,所以实际难度得看那个题先被做出来。 L L L碰巧榜单上先被做出来, 于是做 L L L 的人就更多了。
-
出题人给的题目难度表:
-
题解顺序: A M E B C G L I F H D K AMEBCGLIFHDK AMEBCGLIFHDK
-
由于本博客是用于整理复习,部分简单题只给了代码,部分题给了详细分析及思路历程,部分题有简要分析。( H D K HDK HDK详解)
-
PS: K K K 题看题解有依赖关系构成基环树,想到了今年杭州站的H题,杭州站没做出来,这场更简单的也没做出来,要恶补一下基环树了QAQ。 F F F 考了第二类斯特林数,又学新知识了。
A-DFS搜索 (nowcoder.com)
找字符串子序列,数据范围小,很简单,直接上 AC 代码。
pythom code:
import sys
input = sys.stdin.readline
def solve():
n = int(input())
s = input().strip()
a = s.find("D")
b,c = 0,0
if a == -1:
print("0 ",end = '')
else:
for i in range(a+1,n):
if s[i] == 'F':
b = i
break
if b != 0:
for i in range(b+1,n):
if s[i] == 'S':
c = i
break
if c == 0: print('0 ',end = '')
else: print("1 ",end = '')
else:
print("0 ",end = '')
a = s.find("d")
b,c = 0,0
if a == -1:
print("0")
else:
for i in range(a+1,n):
if s[i] == 'f':
b = i
break
if b != 0:
for i in range(b+1,n):
if s[i] == 's':
c = i
break
if c == 0: print('0')
else: print("1")
else:
print("0")
for _ in range(int(input())):
solve()
M-牛客老粉才知道的秘密 (nowcoder.com)
这道题也很简单,直接看AC代码即可:
import sys
input = sys.stdin.readline
def solve():
n = int(input())
ans = n // 6
if n % 6 == 0:
print(ans)
return
else:
ans += 1
n -= 6
ans += n // 6
print(ans)
for _ in range(int(input())):
solve()
E-本题又主要考察了贪心 (nowcoder.com)
- 题目大意是说给你 n n n 只斗鸡,和初始得分,给定加分规则问 1 1 1 号斗鸡最高能排前几名。
- 本题是想诱导选手考虑贪心,但其实是有问题的,再看看数据范围,已经小的不能再小了~~[笑哭]
直接考虑写个指数级别的暴力就可以AC了,通过DFS实现比较容易(其实递归那里用个循环更优雅[憨笑])~~。
python code:
import sys
input = sys.stdin.readline
sys.setrecursionlimit(5000000)
def solve():
n,m = map(int, input().split())
a = [0] + list(map(int, input().split()))
ls = []
ans = 10
for _ in range(m):
x,y = map(int, input().split())
if x == 1 or y == 1:
a[1] += 3
else:
ls.append((x,y))
def dfs(i):
nonlocal ans
if i == len(ls):
res = 0
for i in range(2,n+1):
if a[i] > a[1]:
res += 1
ans = min(ans,res)
return
x,y = ls[i]
a[x] += 3
dfs(i+1)
a[x] -= 3
a[x] += 1
a[y] += 1
dfs(i+1)
a[x] -= 1
a[y] -= 1
a[y] += 3
dfs(i+1)
a[y] -= 3
dfs(0)
print(ans + 1)
for _ in range(int(input())):
solve()
B-关鸡_2024牛客寒假算法基础集训营1 (nowcoder.com)
- 这题考了点思维,判特殊情况? 感觉怎么搞都行,但也不太好做,比赛情况下写的急了很容易WA,我比赛时候是WA了两发才过的,一发WA后发现要特判,改了交又WA,干脆同思路重写一份AC了,蚌埠住了QAQ。
- 首先,我们的答案只有
0
,
1
,
2
,
3
0,1,2,3
0,1,2,3 这几种可能,这很容易发现,3个的化都可以把鸡围住了。
话说这套题怎么这么喜欢鸡啊(bushi - 本题题解:
- 首先,在读入的时候拿字典存一下 c i c_i ci 对应的 r i r_i ri ,然后枚举一下所有的 c i c_i ci 看一下对应的那一列火点有几个 a a , b b aa,bb aa,bb 分别表示左右两边要拦住所需要的最少火点。
- 我们发现在相邻列的火点如果在不同行,我们其实也不用管,因为也拦死了,所以我们再枚举一边更新即可。
- 接下来就对在 − 1 , 0 , 1 -1,0,1 −1,0,1 这三列存在火点的情况做一个特判即可。
python code:
import sys
input = sys.stdin.readline
def solve():
n = int(input())
dic = {}
for _ in range(n):
r, c = map(int, input().split())
if c in dic:
dic[c].append(r)
else:
dic[c] = [r]
aa, bb = 2, 2
ans = 3
for c in dic:
if c == 0:
aa = min(1, aa)
bb = min(1, bb)
ans = min(aa + bb, ans)
if aa + bb == 0:
print(0)
return
if len(dic[c]) == 2:
if c > 0:
bb = min(0, bb)
else:
aa = min(0, bb)
else:
if c > 0:
bb = min(bb, 1)
else:
aa = min(1, aa)
tmp = sorted(dic.keys())
for i in range(1, len(tmp)):
c1 = tmp[i]
c2 = tmp[i - 1]
if c1 - 1 == c2 and len(dic[c1]) != 2 and len(dic[c2]) != 2:
if dic[c1] != dic[c2]:
if c1 < 0 or c1 == 0:
aa = min(aa, 0)
elif c1 > 0:
bb = min(bb, 0)
break
if aa + bb == 0:
print(0)
return
ans = min(ans, aa + bb)
if (0 not in dic):
if 1 in dic and 1 in dic[1] and -1 in dic and 1 in dic[-1]:
print(1)
return
elif (1 in dic and 1 in dic[1]) or (-1 in dic and 1 in dic[-1]):
ans = min(ans, 2)
print(ans)
for _ in range(int(input())):
solve()
C-按闹分配_2024牛客寒假算法基础集训营1 (nowcoder.com)
- 这题在比赛的时候很快就切了,其实感觉比较简单,根据题意我们能想到使总不满意度最小其实就是办事快的先办事。为什么呢?
- 我们可以发现根据不满意度的定义,在前面做的事会对其后面所有人造成一次不满意度的增加,既然如此,最先做时间短的肯定更优。
- 那么现在我们很容易想到, t c tc tc 插队使得总不满意度增加的值可以 O ( 1 ) O(1) O(1) 计算出,那么我们维护一个前缀和,对于每次询问,在排序后的 t t t 数组二分查找,再 O ( 1 ) O(1) O(1) 输出即可,总的时间复杂度是 O ( q log n ) O(q\log{n}) O(qlogn)。
python code:
import sys
input = sys.stdin.readline
def solve():
def find(x):
l,r = 0,n
while l < r:
mid = (l+r)//2
if (n-mid)*tc <= m: r = mid
else: l = mid + 1
return r
n,q,tc = map(int, input().split())
t = [0] + list(map(int, input().split()))
t.sort()
su = [0]*(n+1)
for i in range(1,n+1):
su[i] = su[i-1] + t[i]
for _ in range(q):
m = int(input())
idx = find(m)
print(su[idx] + tc)
solve()
G-why买外卖_2024牛客寒假算法基础集训营1 (nowcoder.com)
这题其实维护一个前缀和就可以了,直接上代码:
import sys
input = sys.stdin.readline
def solve():
n, m = map(int, input().split())
ls = [(0, 0)]
for _ in range(n):
a, b = map(int, input().split())
ls.append((a, b))
ls.sort(key=lambda x: x[0])
tmp = [0] * (n + 1)
for i in range(1, n + 1):
tmp[i] = tmp[i - 1] + ls[i][1]
idx = -1
for i in range(1, n + 1):
if m >= ls[i][0] - tmp[i]: idx = i
if idx == -1:
print(m)
return
print(ls[idx][0] + m - (ls[idx][0] - tmp[idx]))
for _ in range(int(input())):
solve()
L-要有光_2024牛客寒假算法基础集训营1 (nowcoder.com)
这题比较乱搞,其实绿墙对应的就是等腰三角形的中位线,而阴影部分就是中位线下的等腰梯形,而当光源抬高时到大于
2
h
2h
2h 时,根据生活常识或物理素养可知影子会变短(bushi,当光从打到白墙上到打到地上时,影子面积变小。所以最大值在光源位于
x
o
y
xoy
xoy 平面时最大。
python code:
import sys
input = sys.stdin.readline
def solve():
c,d,h,w = map(int, input().split())
print(3*w*c)
for _ in range(int(input())):
solve()
I-It’s bertrand paradox. Again!_2024牛客寒假算法基础集训营1 (nowcoder.com)
这道题核心思路是:
- 找到两种方法会使得哪个统计量有显著区别,尝试区分这个统计量的均值
- 如果这个统计量不好直接求,我们可以本地真的实现以下两种生成方式,然后生成大量数据,真的随机出这个统计量的真实值。比如求出法1该统计量均值 m 1 m_1 m1、法2该统计量均值 m 2 m_2 m2,那对于输入数据,直接看输入 数据的该统计量 m m m 和 m 1 m_1 m1 与 m 2 m_2 m2 谁更近就行了。
上述是出题人给的比较正规的解法其实这道题是可以不求统计量过的。
- 首先我们要发现两个统计方式会造成结果的差异。
- 第一种 ( x , y ) (x,y) (x,y) 是随机的,之后哪怕 r r r 不满足要求,我们会只重新生成 r r r ,这样会导致其实圆心是在目标区域几乎均匀分布的。
- 第二种,只要不满足就全部重新生成,仔细想想会发现,越接近边缘,成功的概率越小,因为 r r r 得越小,所以圆心会相对集中在考中心位置。
- 在知道上述差异后,我们只需要考虑横纵坐标不超过多少或超过多少的地方计算阈值即可。具体算法是,算出两种方法在自己选定区域的生成成功的概率,通过概率乘以 1 0 5 10^5 105 得到阈值,判断大小即可,当然我比赛时没有正经的算,找了个差不多的阈值就AC了。
python code:
import sys
input = sys.stdin.readline
n = int(input())
se = []
for i in range(100000):
x,y,r = map(int, input().split())
se.append((x,y))
cnt = 0
for x,y in se:
if x >= 95 or y >= 95 or x <= -95 or y <= -95:
cnt += 1
if cnt > 6000:
print("bit-noob")
else:
print("buaa-noob")
F-鸡数题!_2024牛客寒假算法基础集训营1 (nowcoder.com)
这是一道考第二类斯特林数的题,作为一个蒟蒻,比赛前没遇到过斯特林数的题,赛后补了一下斯特林数的知识,模板参考其他牛友的提交。
python code:
class Factorial:
def __init__(self, N, mod) -> None:
self.mod = mod
self.f = [1 for _ in range(N)]
self.g = [1 for _ in range(N)]
for i in range(1, N):
self.f[i] = self.f[i - 1] * i % self.mod
self.g[-1] = pow(self.f[-1], mod - 2, mod)
for i in range(N - 2, -1, -1):
self.g[i] = self.g[i + 1] * (i + 1) % self.mod
def comb(self, n, m):
if n < 0 or m < 0 or n < m:
return 0
return self.f[n] * self.g[m] % self.mod * self.g[n - m] % self.mod
def perm(self, n, m):
if n < 0 or m < 0 or n < m:
return 0
return self.f[n] * self.g[n - m] % self.mod
def catalan(self, n):
# TODO: check 2 * n < N#
return (self.comb(2 * n, n) - self.comb(2 * n, n - 1)) % self.mod
# N个不同的物品放进m个不同的集合,保证每个集合都不是空集#
def sterling(self, n, m):
if n < m:
return 0
else:
res = 0
for i in range(m + 1):
t = self.comb(m, i) * pow(m - i, n, self.mod) % self.mod
if i & 1:
res -= t
else:
res += t
res %= self.mod
return res * self.g[m] % self.mod
n,m = map(int,input().split())
t = Factorial(n+1,int(1e9) + 7)
print(t.sterling(n,m))
H-01背包,但是bit_2024牛客寒假算法基础集训营1 (nowcoder.com)
这道题考察位运算相关知识并结合考察思维和代码实现,这道题给的背景是01背包,但显然不是用普通的背包DP求解,但对于这题我们有一种暴力的解法,枚举所有合法可能,覆盖问题的所有状态空间求解,是个最优化问题。而对于每一种情况为该选择下答案最优,我们肯定是选到不能再选为止,那么我们要解决的核心问题就是怎么根据题目条件,看看是否有什么特殊性质或剪枝优化什么的,得到复杂度可以接受的覆盖所有问题状态空间的方法。
我们来看看详细题解:
- 首先,我们考虑把问题分为两种情况:
- 我们选出的物品的体积按位或运算的结果 c < m c<m c<m。
- 我们选出的物品的体积按位或运算的结果 c = m c=m c=m。
- 对于第二种情况,我们只需要枚举所有的 w w w 判断 w i ∣ m w_i|m wi∣m 是不是等于 m m m 即可。
- 对于第一种情况,我们得先意识到对于 c < m c<m c<m 意味着 m m m 的二进制表示中为 1 1 1 的 bit,在 c c c 中对应位置至少得有一个为 0 0 0,这样其低位无论是 0 还是 1 都会使 c < m c<m c<m,本题只要考虑相差一位的情况。
- 那么我们就可以枚举每一个 m m m 为 1 的 bit,强制 c c c 在该位为 0,对于其低位我们直接取,对于该位的高位部分,我们只取 m m m 的子集,即若要取 w i w_i wi 需满足 w i 该 位 为 1 , 则 m 在 该 位 必 须 为 1 w_i该位为1,则m在该位必须为1 wi该位为1,则m在该位必须为1 (对高位)。
- 这样我们就覆盖了问题的所有可能的状态空间,对求得的值取 m a x max max 就是我们的答案。
python code:
import sys
input = sys.stdin.readline
pw = [1]*64
for i in range(1,64):
pw[i] = pw[i-1]*2
def solve():
def get(t):
ll = []
while t:
ll.append(t&1)
t >>= 1
return ll
n,m = map(int, input().split())
ans = 0
v,w = [0]*(n+1),[0]*(n+1)
for i in range(1,n+1):
v[i],w[i] = map(int, input().split())
ls = get(m)
for i in range(len(ls)):
if not ls[i]: continue
tmp = 0
for j in range(1,n+1):
if w[j] < pw[i] or w[j] == 0: tmp += v[j]
elif w[j] > pw[i]:
tls = get(w[j])
flag = 0
if tls[i] == 1 or len(tls) > len(ls): continue
for k in range(i+1,len(tls)):
if tls[k] and ls[k] == 0:
flag = 1
break
if flag: continue
tmp += v[j]
ans = max(ans,tmp)
tmp = 0
for j in range(1,n+1):
if w[j] | m == m: tmp += v[j]
ans = max(tmp,ans)
print(ans)
for _ in range(int(input())):
solve()
D-数组成鸡_2024牛客寒假算法基础集训营1 (nowcoder.com)
这道题有许多奇怪的暴力做法,正如出题人题解里说的,核心是要注意到:询问的M范围不大,所以数组稍微长 一点儿,就很可能溢出 1 0 9 10^9 109 的范围,所以要考虑的方案其实很少。
这道题我是赛后补的题,思路参考了 j l s jls jls 的思路,下面是解题思路:
- 我们考虑到,其实只要给的数组 a a a 中有超过16个不同的数字那么不管怎么加减最后乘积都是会溢出的,要么为0 (可以自己在草稿纸上简单算一算)。
- 这道了这个之后其实符合条间的就已经不多了,考虑怎么暴力枚举。
- 由于边界是 ± 1 0 9 ±10^9 ±109 ,所以我们只要枚举加减 1 0 9 \sqrt{10^9} 109 的情况即可,具体实现看代码注释。
python code
import sys
input = sys.stdin.readline
rg = int(4e4) # range(值域):4e4 > sqrt(1e9)
inf = int(1e9) # 边界
def solve():
n,q = map(int, input().split())
a = list(map(int, input().split()))
m = list(map(int, input().split()))
dic = {}
for i in range(n): #统计
if a[i] in dic: dic[a[i]] += 1
else: dic[a[i]] = 1
n = len(dic)
ans,ls = set(),sorted(dic.keys())
ans.add(0)
if n <= 17:
for i in range(n):
x = ls[0] - rg
for j in range(max(x,ls[i]-rg),ls[i]+rg+1): #取max避免重复计算
res = 1
for k in range(n):
val = ls[k] - j
for c in range(dic[ls[k]]):
res *= val
if res == 0 or abs(res) > inf: break
if res == 0 or abs(res) > inf: break
if res == 0 or abs(res) > inf: continue
ans.add(res)
for i in range(q):
print("Yes" if m[i] in ans else "No")
solve()
K-牛镇公务员考试_2024牛客寒假算法基础集训营1 (nowcoder.com)
python code:
import sys
from collections import deque
input = sys.stdin.readline
mod = 998244353
def solve():
def toposort():
q = deque()
for i in range(1, n + 1):
if deg[i] == 0: q.append(i)
while q:
x = q.popleft()
y = g[x][0][0]
deg[y] -= 1
if deg[y] == 0:
q.append(y)
def cheak(x, op):
now, end = x, x
tmop = op
while g[now][0][0] != end:
vis[g[now][0][0]] = 1
tmop = ls[g[now][0][1][tmop]]
now = g[now][0][0]
return 0 if ls[g[now][0][1][tmop]] != op else 1
n = int(input())
g = [[] for _ in range(n + 1)]
deg = [0] * (n + 1)
for i in range(1, n + 1):
a, s = input().strip().split()
g[i].append((int(a), s))
deg[int(a)] += 1
ans = 1
vis = [0] * (n + 1)
ls = {'A': 0, 'B': 1, 'C': 2, 'D': 3, 'E': 4}
toposort()
vis = [0] * (n + 1)
for i in range(1, n + 1):
if deg[i] != 0 and not vis[i]:
vis[i] = 1
tmp = 0
for j in ls.values():
tmp += cheak(i, j)
ans *= tmp
ans %= mod
print(ans)
solve()