AtCoderのSwiftでセグ木をはじめる - 2025版
ここでは区間和取得を例にします。
ライブラリをインポートするため、以下を追加します。
import AtCoder
モノイドを表す型を用意し、単位元 e や演算対象となる型 S を決めます。
enum Example { typealias S = Int }
SegTreeOperatorプロトコルを継承します。
enum Example: SegTreeOperator { typealias S = Int }
この時点ではコンパイルエラーになりますが、Add stubs for conformanceという選択肢が増えるので、
Applyを選択すると不足している実装を Xcode が補完してくれます。
enum Example: SegTreeOperator { static func op(_ x: Int, _ y: Int) -> Int { <#code#> } static let e: Int typealias S = Int }
必要な項目を埋めると以下のようになります。
enum Example: SegTreeOperator { /// 結合的な二項演算子 static func op(_ x: Int, _ y: Int) -> Int { x + y } /// その演算の単位元 static let e: Int = 0 typealias S = Int }
モノイドを指定してSegTreeを利用します。
let N = Int.stdin let A = [Int].stdin(columns: N) nonisolated(unsafe) var example = SegTree<Example>(A)
新しいモノイドを書く場合、Sから決めていくのがコツです。
他、簡単な例を載せておきます。ご自由にお使いください。
/// 区間最小値取得 enum Min: SegTreeOperator { static func op(_ x: Int, _ y: Int) -> Int { min(x, y) } static let e: Int = .max } /// 区間最大値取得 enum Max: SegTreeOperator { static func op(_ x: Int, _ y: Int) -> Int { max(x, y) } static let e: Int = .min } /// 区間和取得 enum Sum: SegTreeOperator { static func op(_ x: Int, _ y: Int) -> Int { x + y } static let e: Int = 0 }
AtCoderのSwiftをXcodeではじめる - 2025版
macOS を利用していて、Xcode 26.0 以降(Swift 6.2 以降)がインストール済みであることを前提とします。
GitHub の atcoder-2025-swift を開きます。
https://github.com/narumij/atcoder-2025-swift
緑色の「Code」ボタンをクリックし、「Download ZIP」を選択します。
これにより、パッケージ一式がダウンロードされます。
ダウンロードした ZIP ファイルを解凍します。 解凍したフォルダを開きます。
Package.swift をダブルクリックします。
Xcode が起動し、Macro の実行許可を求めるダイアログが表示された場合は「Trust and Open」を選択してください。
Sources/main.swift の内容を削除し、提出するコードを記述します。
これで AtCoder の Swift 環境に近い開発環境を利用できます。
AtCoderのSwiftで入力処理が少し楽になった話
AtCoder Language Update 2025では搭載ライブラリが増えており、標準入力を扱うライブラリもその一つです。
例えば、入力は以下の形式で標準入力から与えられる。という場合、
N M
以下のよう書くことで、標準入力を読むことができます。
import AcFoundation var N = Int.stdin var M = Int.stdin
他に、入力は以下の形式で標準入力から与えられる。という場合、
N A1 A2 ... AN B1 B2 ... BN
以下のよう書くことで、標準入力を読むことができます。
import AcFoundation let N = Int.stdin let A = [Int].stdin(columns: N) let B = [Int].stdin(columns: N)
practice contest A問題の提出例は以下になります。 https://atcoder.jp/contests/practice/tasks/practice_1
(真面目に書いた場合)
import AcFoundation let a = Int.stdin let b = Int.stdin let c = Int.stdin let s = String.stdin print(a + b + c, s)
(型推論と省略記法を用いて簡潔に書いた場合)
import AcFoundation print(Int.stdin + .stdin + .stdin, String.stdin)
標準エラーへの出力(Swift)
特にAHCでバグってしまった場合など、競プロに参加していると、標準エラーに文字列を出力したくなる場合があります。 以下のエクステンションを使うことで、print関数のto:パラメータに直接stderrを渡せるようになります。
extension UnsafeMutablePointer: TextOutputStream where Pointee == FILE { public mutating func write(_ string: String) { guard let data = string.data(using: .utf8) else { return } _ = data.withUnsafeBytes { bytes in #if os(Linux) Glibc.write(fileno(self), bytes.baseAddress!, data.count) #else Darwin.write(fileno(self), bytes.baseAddress!, data.count) #endif } } }
使い方は以下のように。
print("Hello, STDERR!", 1, 2, 3, 4, to: &stderr)
既出の内容ですが、print関数の自由度を損ないたくなかったので、stderrに生やしてみました。 どうぞご自由に。
茶色になりました。
AtCoderで茶色になりました。使用言語は主にSwiftです。
ABC324から、ABC332までの、約2ヶ月ほどでの入茶でした。
きっかけは同僚の「すぐ緑になれそう」という一言です。
その後、どちらが少ない出場回数で緑になれるか競争しています。
ここでは、課題に感じたことと、課題への取り組みについて書きたいと思います。
まず、デビュー戦では、暗黙のコピーが発生した際のパフォーマンス低下に苦しみました。特に文字列です。
文字に関してもSwiftがリッチなことで、C++と比べてパフォーマンスが低く、これをどう回避するかに取り組みました。
方針として、文字を触る必要がある場合にはutf8CStringメソッドで変換し、CCharを使うことにしました。
次に作業性の悪さの問題がありました。いろいろ探してみたのですが、自分にはしっくりこなかったので、ojtとSwift Packageを、シェルスクリプトとMakefileを用いて、テストやデバッグや送信ができるように整えました。
その後、upsolveでのライブラリの不足に直面します。ついかっとなって、AcLibraryを勝手に移植しました。以下です。
https://github.com/narumij/swift-ac-library
すでに移植されたものはGithubにみつかったのですが、テストコードがしっかり通っているモノがどうしても欲しかったので、新たに移植する選択をしました。
移植できたライブラリいくつか試したところ、どうしてもTLEしてしまう問題に遭遇しました。
いろいろ試した結果、入力処理だけで制限時間の8割ほどに達していることがわかり、getchar_unlockedを用いた入力ライブラリを作成し、セオリーのreadLineを卒業しました。出力にputchar_unlockedを用いることで、処理時間を稼げる場合もありますが、文字列をutf8CStringにするコストが悪い方に効いて性能悪化する場合もあるので、こちらは採用しませんでした。
他に、平衡二分探索木も勝手に移植していますが、こちらは実験レベルにとどまっています。
関連書籍として、鉄則本を最初の一冊目に選び、今のところこの一冊だけで取り組んでいます。次はけんちょん本を買い足したいです。
1ヶ月半がすぎ、ある程度コンテストに対して慣れたわりにレートが伸び悩んでいて、
問題文を理解したがらない自分の脳みそを課題と感じ、A問題10問を毎日解く取り組みを三日ほどしました。
さらに、レベルアップを望んで、D問題への取り組み始めました。
そんな矢先の、入茶でした。
もともとPaizaでAをとっていたので、茶色になれると踏んではいたのですが、実際に到達できて一安心です。
Swift固有の問題にまだまだ遭遇したり、苦しんだりしそうですが、ゴールは緑、夢は水色で、この先も精進します。
SSとかは省略します。あしからずご了承ください。
UnitTestで標準入力の差し替え
過去問やってると、(Command+Uが染みついてて)UnitTestつかいたくなるし、かといって入力をいちいち手作業でリテラルにするのはおっくうで。
そこで!
struct AtCoderRunner { typealias Print = (String) -> Void typealias Solver = (Print) -> Void let solver: Solver func run(input: String) -> String { var output: [String] = [] var buffer: [Int8] = input.utf8CString + [0] let count = buffer.count buffer.withUnsafeMutableBytes { let file = fmemopen($0.baseAddress, count, "r") assert(file != nil) let backup = stdin stdin = file! solver() { output.append($0) } stdin = backup } return output.joined(separator: "\n") } }
例えばこんな解答関数があったとすると
func TemplateSolver(print: (String) -> Void) { let N = Int(Swift.readLine()!)! print("\(N)") }
こんな風にテストできます。
final class AtCoderTemplateTests: XCTestCase { var stdinCopy: UnsafeMutablePointer<FILE>? override func setUpWithError() throws { // 念のため stdinCopy = stdin // でもテストが並列で動くとおじゃん } override func tearDownWithError() throws { // 念のため stdin = stdinCopy! // でもテストが並列で動くとおじゃん } // Greenだよー func testTemplate() throws { XCTAssertEqual( AtCoderRunner(solver: TemplateSolver) .run(input: """ 0 """), """ 0 """) } }
提出するときは、雑に説明するとこんな感じです。
TemplateSolver {
print($0)
}
どうぞご自由におつかいください。
(いまさらですが、過去記事も同様です)
あ、Swiftです。
lowerBound
★3の過去問やった際に書いた、SwiftのlowerBoundを今後も使いそうなのでメモ
extension RandomAccessCollection where Element: Comparable { func lowerBound(of element: Element) -> Index { guard first! < element else { return startIndex } var (left, right) = (startIndex, endIndex) while left < right { let mid = index(left, offsetBy: distance(from: left, to: right) / 2) if self[mid] < element { left = mid } else { right = mid } } return left == endIndex ? index(endIndex, offsetBy: -1) : left } }