1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

茶色コーダによるABC379振り返り

Last updated at Posted at 2024-11-10

はじめに

前回マジで投稿遅れてすいません(やっと反省した)
今回はコンテスト翌日の投稿です。

結果としてはABDの3完でパフォは800ちょいでした。
予想通りCが難しくなってて、すぐパスできて良かったです
えーと ものすごく申し上げにくいのですがBで4ペナくらいましたw(よく緑パフォでたな)
Dはすぐに解法が思いついたものの二分探索がうまくいかず、結構実装に手間取ってしまいました
AJLの中1参加者が、C問題解けてる割合が結構高くてなんの差だろうと、考えています(明らかに数学とかの差だろ)

使用しているライブラリ

グラフのクラスと、複数行入力をサポートするライブラリを追加しました
githubのPerformansの.template/codeにおいてあります じっくり見たい方はそちらを見てください

# ライブラリと関数と便利変数
# ライブラリ
from collections import deque, defaultdict, Counter
from math import pi
from itertools import permutations
import bisect
import sys
import heapq
from typing import List

# cortedcontainersは使うときだけ wandbox非対応なので
# from sortedcontainers import SortedDict, SortedSet, SortedList

sys.setrecursionlimit(5 * 10**5)


# 関数
def pow(x: int, n: int, t: int = 1):
    # O(log N)
    if t == 1:
        ans = 1
        while n:
            if n % 2:
                ans = ans * x
            x = x * x
            n >>= 1
        return ans
    ans = 1
    while n:
        if n % 2:
            ans = (ans * x) % t
        x = (x * x) % t
        n >>= 1
    return ans


def is_prime(n: int) -> bool:
    # O(√N)
    if n == 1:
        return False

    i = 2
    s = n**0.5

    while i < s:
        if n % i == 0:
            return False

        i += 1

    return True


def eratosthenes(n):
    primes = [True] * (n + 1)
    primes[0], primes[1] = False, False
    i = 2
    while i**2 <= n:
        if primes[i]:
            for k in range(i * 2, n + 1, i):
                primes[k] = False

        i += 1

    return [i for i, p in enumerate(primes) if p]


def gcd(a, b):
    while a > 0 and b > 0:
        if a > b:
            a = a % b
        else:
            b = b % a

    return max(a, b)


def lcm(a, b):
    return (a * b) // gcd(a, b)


def calc_divisors(N):
    # 約数全列挙
    result = []

    for i in range(1, N + 1):
        if i * i > N:
            break

        if N % i != 0:
            continue

        heapq.heappush(result, i)
        if N // i != i:
            heapq.heappush(result, N // i)

    return result


def factorization(n):
    # 素因数分解
    result = []
    tmp = n
    for i in range(2, int(-(-(n**0.5) // 1)) + 1):
        if tmp % i == 0:
            cnt = 0
            while tmp % i == 0:
                cnt += 1
                tmp //= i
            result.append([i, cnt])

    if tmp != 1:
        result.append([tmp, 1])

    if result == []:
        result.append([n, 1])

    return result


# 標準入力系
# 一行に一つのstring
def s():
    return sys.stdin.readline().rstrip()


# 一行に複数のstring
def sl():
    return s().split()


# 一つのint
def ii():
    return int(s())


# 一行に複数のint
def il(add_num: int = 0):
    return list(map(lambda i: int(i) + add_num, sl()))


# 複数行の入力をサポート
def li(n: int, func, *args):
    return [func(*args) for _ in [0] * n]


# 自作型
class Heap:
    def __init__(self) -> None:
        self.heap: List[int] = []

    def push(self, x: int):
        """
        値を挿入する関数

        計算量 O(log N)
        Nはheapのサイズ
        """

        # 末尾に挿入
        self.heap.append(x)
        # 現在地の変数
        ind: int = len(self.heap) - 1

        # 更新処理
        while ind > 0:
            # 現在地の親を求める
            # 求め方はセグ木の要領で
            parent: int = (ind - 1) // 2

            # 親の方が小さかったら処理を終了
            if self.heap[parent] <= x:
                break

            # 親より小さかったら入れ替える
            self.heap[ind] = self.heap[parent]
            # 現在地を更新
            ind = parent

        # 最後に現在地にxを代入
        self.heap[ind] = x

    def min(self):
        """
        最小値を出力する関数

        計算量
        O(1)
        """
        if len(self.heap) == 0:
            print("\033[31m error heap is empty \033[0m")
            return
        return self.heap[0]

    def pop(self):
        """
        最小値を削除する関数
        返り値は削除した最小値

        計算量
        N = len(self.heap)
        O(log N)
        """
        # 返り値を取っておく
        result = self.min()

        # self.heapが空ならエラーを吐く
        if len(self.heap) == 0:
            print("\033[31m error heap is empty \033[0m")
            return

        # 末尾の値を取得
        x: int = self.heap.pop()
        # 現在地
        ind = 0

        # 要素がなくなった場合それが最小値なので出力する
        if len(self.heap) == 0:
            return result

        # 更新処理
        while ind * 2 + 1 < len(self.heap):
            # 現在地の子のインデックスを変数に入れとく
            child1 = ind * 2 + 1
            child2 = ind * 2 + 2
            # child2の要素がchild1の要素より小さければchild1をchild2にする
            if child2 < len(self.heap) and self.heap[child2] < self.heap[child1]:
                child1 = child2

            # child1の要素がxより大きければ処理を終了
            if self.heap[child1] >= x:
                break

            # 入れ替え
            self.heap[ind] = self.heap[child1]

            # 現在地を移動
            ind = child1

        # 現在地の要素をxにする
        self.heap[ind] = x

        # 削除した値を返す
        return result


# 無向グラフ
class Graph:
    def __init__(self, N: int, dire: bool = False) -> None:
        self.N = N
        self.dire = dire
        self.grath = [[] for _ in [0] * self.N]

    def new_side(self, a: int, b: int):
        # 注意 0-indexedが前提
        self.grath[a].append(b)
        if not self.dire:
            self.grath[b].append(a)

    def side_input(self):
        # 新しい辺をinput
        a, b = il(-1)
        self.new_side(a, b)

    def input(self, M: int):
        # 複数行の辺のinput
        for _ in [0] * M:
            self.side_input()

    def get(self, a: int):
        # 頂点aの隣接点を出力
        return self.grath[a]

    def all(self):
        # グラフの内容をすべて出力
        return self.grath


# 有向グラフ
class GraphW:
    def __init__(self, N: int, dire: bool = False) -> None:
        self.N = N
        self.dire = dire
        self.grath = [[] for _ in [0] * self.N]

    def new_side(self, a: int, b: int, w: int):
        # 注意 0-indexedが前提
        self.grath[a].append((b, w))
        if not self.dire:
            self.grath[b].append((a, w))

    def side_input(self):
        # 新しい辺をinput
        a, b, w = il(-1)
        self.new_side(a, b, w)

    def input(self, M: int):
        # 複数行の辺のinput
        for _ in [0] * M:
            self.side_input()

    def get(self, a: int):
        # 頂点aの隣接点を出力
        return self.grath[a]

    def all(self):
        # グラフの内容をすべて出力
        return self.grath


# 便利変数
INF = 10**18
lowerlist = list("abcdefghijklmnopqrstuvwxyz")
upperlist = list("ABCDEFGHIJKLMNOPQRSTUVWXYZ")


# テンプレ
class SegmentTree:
    # 鉄則本のパクリですけどよろしく
    def __init__(self, N) -> None:
        # サイズは要素の数

        self.size = 1
        while self.size < N:
            self.size *= 2

        self.data = [0] * (self.size * 2)

    def update(self, ind, x):
        ind = ind + self.size - 1
        self.data[ind] = x

        while ind >= 2:
            ind //= 2
            self.data[ind] = max(self.data[ind * 2], self.data[ind * 2 + 1])

    def query(self, l, r, a, b, u):
        if r <= a or l >= b:
            return -INF
        if l <= a and b <= r:
            return self.data[u]

        m = (a + b) // 2
        return max(self.query(l, r, a, m, u * 2), self.query(l, r, m, b, u * 2 + 1))


# コード

ほんじゃ一問ずつ、感想書きますか

A問題

問題文を要約
三桁の整数を右からa,b,cとしたときb,c,aを並び替えた整数とc,a,bを並び替えた整数を空白区切りで、出力してください。
みたいな感じの問題でした

まあササッと入れ替えれば簡単です
ACコード(ライブラリ抜粋) 全文
関数名はご想像にお任せします

a, b, c = list(s())
print(b + c + a, c + a + b)

おい過去一短いんじゃないか(ライブラリが増えて8000byteありますけどね)

B問題

問題文を要約
$S$の$i$文字目がOのとき、左から$i$番目がOなら歯は丈夫です。Xの場合は虫歯です

連続する$K$本の歯が丈夫なら、その$K$本の歯を使っていちごを一個食べられます。ですが、いちごを食べるとその$K$本の歯は虫歯になって使えなくなります
最大で何個のいちごが食べられますか?

普通にいちご一個食べるだけ虫歯になるなんて、怖いなおい
全然要約できてないじゃんw

普通に貪欲で解いた。
だけど調整ミスって、4ペナした(調整苦手)

ACコード(ライブラリ抜粋) 全文

N, K = il()
S = list(s())

L = [S[i] == "O" for i in range(N)]

ans = 0
i = 0

while i <= N - K:
    if all(L[i : i + K]):
        ans += 1
        for k in range(i, i + K):
            L[k] = False

        i += K - 1
        continue
    i += 1

print(ans)

C問題

問題文を要約
N個のマスが、あります
最初、M個のマスに、石が入っておおり、マス$X_i$には$A_i$個の石が入っています。
あなたは何回でも、以下の操作を、行うことができます
マス$i$に石が、あるならマス$i+1$に石を、移動させる

すべてのマスに石が入るように、するために、最小何回操作が必要か、求めてください。また、不可能な場合は-1を出力してください

全然要約できてないじゃんw(二回目)

貪欲かなーって、考えたりしたけど、結局ACできなかった。(結構いい線行ってた)
AJLの同学年(中1)は結構ACしていて、泣きかけた

D問題

問題文を要約 やっても無駄そうだしやらない(疲れてる)

偽解法で、ACした。(公式解説にも書いてない) そして計算量は$O(Q log Q)$ぐらい公式解説は、$O(Q)$なんこれw

まあ、解けた解法書くか
多分計算量は、落ちるけど、わかりやすいと思う
解法は、二分探索系

クエリ1は、
何日伸びたか、取っておいてその変数$\times -1$した値を、appendする
クエリ2は、
何日伸びたかの変数に、$t$を足す
クエリ3は、
二分探索して、$ok$を出力して、リストには、ok以降の値を残す
具体的には、$L_{\text{mid}}$+何日伸びたかの変数を、足して判定する
あとは普通に二分探索

公式解説よりは、スマートにかけたと思います(公式解説C++だぞ)
なんか今回いつにもよらずスマートに、かけたと思います
前回、クソコード炸裂してたじゃないか

acコード 全文

Q = ii()
L = []
t = 0

for _ in [0] * Q:
    l = il()
    match l[0]:
        case 1:
            L.append(-t)

        case 2:
            t += l[1]

        case 3:
            ng, ok = -1, len(L)

            while abs(ok - ng) > 1:
                mid = (ng + ok) // 2

                if L[mid] + t >= l[1]:
                    ng = mid
                else:
                    ok = mid

            print(ok)
            L = L[ok:]

最後に

レートは32上がりました。現在レートは、572です
今日はAHC39を頑張りたいと思います。
用事があって、おそらく1時間半遅れの参加なんですけどね

長々と見ていただいて、ありがとうございます

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?