[atc复盘] abc296 20230401
一、本场总结
- 第一次打atcoder,abc做到F。
- A 模拟。
- B 下标计算。
- C 哈希表。
- D 数学贪心枚举。
- E 看似是博弈其实是拓扑排序。
- F 模拟/逆序对。
二、 A - Alternately
链接: A - Alternately
1. 题目描述
2. 思路分析
- 直接枚举每个相邻对是否不同即可。
3. 代码实现
# Problem: A - Alternately
# Contest: AtCoder - AtCoder Beginner Contest 296
# URL: https://atcoder.jp/contests/abc296/tasks/abc296_a
# Memory Limit: 1024 MB
# Time Limit: 2000 ms
import sys
import bisect
import random
import io, os
from bisect import *
from collections import *
from contextlib import redirect_stdout
from itertools import *
from array import *
from functools import lru_cache
from types import GeneratorType
from heapq import *
from math import sqrt, gcd, inf
if sys.version >= '3.8': # ACW没有comb
from math import comb
RI = lambda: map(int, sys.stdin.buffer.readline().split())
RS = lambda: map(bytes.decode, sys.stdin.buffer.readline().strip().split())
RILST = lambda: list(RI())
DEBUG = lambda *x: sys.stderr.write(f'{
str(x)}\n')
MOD = 10 ** 9 + 7
PROBLEM = """https://atcoder.jp/contests/abc296/tasks/abc296_a
给你整数n和长为n的字符串s
s由M/F两种字母组成代表男女。
检测s是否是男女交替的。
"""
def solve():
n, = RI()
s, = RS()
for i in range(n - 1):
if s[i] == s[i + 1]:
return print('No')
print('Yes')
三、B - Chessboard
链接: B - Chessboard
1. 题目描述
2. 思路分析
- 枚举找下标,转换一下下标即可。
3. 代码实现
PROBLEM = """https://atcoder.jp/contests/abc296/tasks/abc296_b
给你一个8*8的棋盘。由'.*'两种字符组成。
棋盘上只有一个*,找出*的坐标。
其中横坐标从下往上编码为1-8;纵坐标从左到右编码为a-h。
"""
# ms
def solve():
g = []
for _ in range(8):
s, = RS()
g.append(s)
for i in range(8):
for j, c in enumerate(g[i]):
if c == '*':
a = chr(ord('a') + j)
print(f"{
a}{
8 - i}")
四、C - Gap Existence
1. 题目描述
2. 思路分析
- 直接把a转成set。
- 枚举每个数,检查和它差为d的数据是否在set里。
3. 代码实现
PROBLEM = """https://atcoder.jp/contests/abc296/tasks/abc296_c
给你整数N(2<=N<=2e5),X(-1e9<=X<=1e9),和长为N的数组A。
请问A中是否存在两个数差值为X。这两个数可以是同一个数。
"""
# ms
def solve():
n, x = RI()
a = set(RILST())
for v in a:
if v - x in a or v + x in a:
return print('Yes')
print('No')
五、D - M<=ab
链接: D - M<=ab
1. 题目描述
2. 思路分析
- 由于数据范围大,直接枚举肯定不行。可以先考虑无解的情况:
- 如果n*n<m肯定无解,因为即使a、b都取到n,乘积也够不到m。
- 如果n*n=m,那ab只能都取到n,任意数再小都不够m,则乘积只能是m。
- 若m<=n,显然ab可以是1 m,最小就是m,返回m即可。
- 其它情况(n*n>m&&n<m)则需要遍历尝试:
- 首先初始化ans上界,起码ab可以取到n*n,这个数是满足条件的答案。我们枚举找比它更小的答案。
- 枚举乘积答案肯定是不行的,考虑枚举因子a/b里小的那个(因为乘法满足交换律,枚举哪个都可以),设为a。
- a显然不会超过sqrt(m),这时可以计算b=ceil(m/a)。上取整后就可以保证a*b>=m。
- 因此这么枚举,只需要保证ab<=n即可更新答案。
- 补充一下为什么不需要枚举>sqrt(m)的部分:
- 如果a>sqrt(m),那么当b=sqrt(m)则乘积一定超过m,为了取到最小的b=ceil(m/a),b只会更小,那这个b一定在之前枚举过了(作为a枚举),或者说这个数对已经被枚举过了。
3. 代码实现
PROBLEM = """https://atcoder.jp/contests/abc296/tasks/abc296_d
给你两个正整数n,m(1<=N,M<=1e12)。
找到一个最小的x,使x满足:
1.x>=m
2.x可以被分解为两个数ab的乘积,其中1<=a,b<=n
"""
# ms
def solve():
n, m = RI()
if n * n < m:
return print(-1)
if n * n == m:
return print(m)
if m <= n:
return print(m)
s = int(m ** 0.5)
ans = n * n
for a in range(2, min(s, n) + 1):
b = (m + a - 1) // a
if b <= n:
ans = min(ans, b * a)
print(ans)
六、E - Transition Game
1. 题目描述
2. 思路分析
这题读题没读懂,就去做F了,赛后补的E。主要是没看懂那个x是哪来的,后来知道写在黑板上的数就是x。而且黑板上每次只有一个数。
- 手玩一下发现:替换操作就是把数字沿着
值作为下标
->值
这个路径往后走,路径可以用图考虑。 - 把整个数组按这个操作建图,图中会存在环,n个节点n条边;而且这个图是内向的,因为出度最多是1.即进了环就出不来。
- 由于第i轮操作的目标数字是知道的,即i,B的目标是让路径的终点是i,那么若i在环上,B可以调整起始点的位置,使ki步后正好到达i。(因为在环上代表前边步数可以无限)
- A的目标是让i不在终点,那么对于所有没进环上的i,可以设置ki>=n足够大,使终点进环。
- 因此对于所有在环上的i,B必赢;不在环上的点,A必赢。
- 找所有环上的点数量不好弄;反过来求不在环上的点,用拓扑排序即可。
3. 代码实现
PROBLEM = """https://atcoder.jp/contests/abc296/tasks/abc296_e
给你N和长度为N的数组a,其中1<=a[i]<=N,下标从1开始。
A和B做N轮游戏,轮数下标从1开始。在第i轮,玩法如下:
1. A指定一个正整数ki,告诉B。
2. B指定一个1~N之间的数,写在黑板上。
3. 做如下操作ki次:把黑板上的数字x替换成a[x]。
操作结束后,如果黑板上的数字是i,B赢;否则A赢。
请问在A和B都做最优操作的情况下,B能赢多少次。
"""
# ms
def solve():
n, = RI()
a = RILST()
g = [[] for _ in range(n + 1)]
degree = [0] * (n + 1)
for i, v in enumerate(a, start=1):
g[i].append(v)
degree[v] += 1
q = deque([i for i, v in enumerate(degree) if i and v == 0])
ans = n
while q:
ans -= 1
u = q.popleft()
for v in g[u]:
degree[v] -= 1
if degree[v] == 0:
q.append(v)
print(ans)
七、F - Simultaneous Swap
1. 题目描述
2. 思路分析
群里大佬说正解是逆序对,我没看懂,直接找性质做的。没想到直接交就TLE了,优化了一下就过了
这里吐槽一下for x in set:break 是会TLE的,看来虽然提前break但没用,依然是O(n)。
- 首先a和b的元素对应数量必须相同,用sorted(a) != sorted(b)特判。
- 手玩发现,如果有1个元素出现两次,那么按照case1的方法交换,一定可以使另外一个位置相同,此时必Yes。
- 然后是模拟验证所有元素只出现一次的情况:
- 使第一个位置元素相同,只需找到一个还没处理的数字,找到它在ab分别的位置,把这个元素换过来。
- 然后依次处理第2\3\4…个位置。如果遇到本来就相同就不用动。
- 直到处理完倒数第三个位置,这时后两个位置如果不同则不行,如果完全相同才行,因为他们无法做出有用的交换了。
- 逆序对做法:如果ab的逆序对数奇偶性相同,则可以交换;否则不能交换。
- 当然前几个特判依然需要。
3. 代码实现
PROBLEM = """https://atcoder.jp/contests/abc296/tasks/abc296_f
给你N(3<=N<=2e5),和两个长度为n的数组a,b。
你可以做如下操作任意次或不做:
选下标j!=i!=k,同时交换:在a中交换a[i]a[j].再b中交换b[i]b[k]
若可以使a完全等于b输出Yes,否则输出No。
"""
# ms
def solve():
n, = RI()
a = RILST()
b = RILST()
if a == b:
return print('Yes')
if sorted(a) != sorted(b):
return print('No')
if len(set(a)) < n:
return print('Yes')
posa = {
v: i for i, v in enumerate(a)}
posb = {
v: i for i, v in enumerate(b)}
left = set(a)
def get(x, y):
s = set()
ans = 0
nonlocal left
for _ in range(3):
p = left.pop()
if x != p != y:
ans = p
s.add(p)
left |= s
return ans
for i in range(n - 2):
if a[i] == b[i]:
left.remove(a[i])
else:
v = get(a[i], b[i])
pa = posa[v]
pb = posb[v]
left.remove(v)
posa[a[i]] = pa
posb[b[i]] = pb
a[pa] = a[i]
b[pb] = b[i]
if a[-1] == b[-1] and a[-2] == b[-2]:
return print('Yes')
print('No')