競プロ向けNim記述

入門用メモ。

散発的になりそう。

import

競プロで使いそうなモジュール

module名主な機能公式Doc
strutils文字列全般: 各種parse, split, join, 検索, format etc.Module strutils
sequtilsmap, zip, filter, fold etc.Module sequtils
math三角関数, gcd, lcm, pow, sqrt, binom, factorial etc.Module math
algorithmsort, reverse, 二分探索(lowerBound, upperBound), 順列イテレータModule algorithm
tablesハッシュマップ(Table, OrderedTable, CountTable)Module tables
sets集合(HashSet, OrderedSet)Module sets
lists単方向リスト, 双方向リストModule lists
queuesキューModule queues
intsetsビットセットModule intsets
critbitscrit-bit木(効率のよい文字列検索木)Module critbits
future実験的な機能(リスト内包表記 etc.)Module future

まとめて

import strutils, sequtils, math, algorithm, tables, sets, lists, queues, intsets, critbits, future

入力

let
    # 文字列
    s = stdin.readLine
    
    # 単一数値
    n = stdin.readLine.parseInt
    
    # 数列
    a = stdin.readLine.split.map(parseInt)
    
    # n行にわたる単一数値
    b = (0..<n).mapIt(stdin.readLine.parseInt)
    
    # n行にわたる数列
    f = (0..<n).mapIt(stdin.readLine.split.map(parseInt))

Nimでは 何か.関数(A, B) という記述で 関数(何か, A, B) が呼ばれるので、関数の入れ子を極力避ける書き方ができる。

# 複数の数値の分割代入
var
    a, b, c: int
(a, b, c) = stdin.readLine.split.map(parseInt)

分割代入(Destructuring assignment)は、直接行うにはこうするしかない?

関数を定義しておけば、letでも受け取れる(参考: somq14様)

proc readSeq(): seq[string] =
    stdin.readLine.split

proc readInt1(): int =
    stdin.readLine.parseInt
proc readInt2(): (int, int) =
    let a = readSeq().map(parseInt)
    return (a[0], a[1])
proc readInt3(): (int, int, int) =
    let a = readSeq().map(parseInt)
    return (a[0], a[1], a[2])
proc readInt4(): (int, int, int, int) =
    let a = readSeq().map(parseInt)
    return (a[0], a[1], a[2], a[3])

let
    (a, b, c) = readInt3()
echo a, b, c

出力

# 単一数値
echo n

# 配列スペース区切り('$'で文字列に出来る型なら可)
echo a.join(" ")

# 配列スペース区切り(ver.0.13)(明示的に文字列に変換の必要あり)
echo a.map(proc (x: int): string = $x).join(" ")

# 複数数値スペース区切り
echo n, " ", m, " ", k
# または
echo "$1 $2 $3" % [$n, $m, $k]

リストの作成

arrayが固定長配列(コンパイル時に長さがわかっている必要がある)、seqが可変長リスト

# 空の1次元Seq
var aaa: seq[int] = @[]  # 型の自動推定が働かないため明言する必要がある
# または
var aaa = newSeq[int]()

# N要素の1次元Seq(その型のデフォルト値で初期化)
var aaa = newSeq[int](N)

# N要素の1次元Seq(その型のデフォルト値以外で初期化)
var aaa = newSeq[int](N)
aaa.fill(x)

# N行M列の2次元Seq
var aaa = newSeq[seq[int]](N)
aaa.fill(newSeq[int](M))
# または
var aaa = newSeqWith(N, newSeq[int](M))

配列の値埋めについては、newSeqWithより、fillした方が速いらしい。これは、randなどが渡されたとき、fillだと引数として渡す際に評価された1つの値が全て入ってしまうが、newSeqWithは毎回initして別々の要素を作成するなどの挙動の違いにより、1要素の生成コストがfillの方が小さいため。

import sequtils, algorithm, random

var aaa = newSeq[int](10)
aaa.fill(rand(100))
echo $aaa
# => @[3, 3, 3, 3, 3, 3, 3, 3, 3, 3]

var bbb = newSeqWith(10, rand(100))
echo $bbb
# => @[89, 18, 2, 95, 20, 9, 54, 37, 80, 51]


# 2次元配列の作成などは、fillでも独立した要素が作られる模様
# (参照が同じでccc[0][0]を変更したらccc[1][0], ccc[2][0]も変更されてしまう、ということはない)

var ccc = newSeq[seq[int]](3)
ccc.fill(newSeq[int](3))
ccc[0][0] = 5
echo $ccc
# => @[@[5, 0, 0], @[0, 0, 0], @[0, 0, 0]]

var ddd = newSeqWith(3, newSeq[int](3))
ddd[0][0] = 5
echo $ddd
# => @[@[5, 0, 0], @[0, 0, 0], @[0, 0, 0]]

要素数の数え上げ

Pythonのitertools.Counterみたいな。

[1, 2, 3, 2, 3, 3, 3]
↓
{1: 1,  2: 2,  3: 4}

CountTableが使える。

import tables

let
    a = @[1, 2, 3, 2, 3, 3, 3]
    b = a.toCountTable
echo b

# {1: 1,  2: 2,  3: 4}
注意点1

AtCoderのver.0.13では、toCountTable() が要素を数え上げない(存在する要素のkeyは作られるが、全てのカウントが1になる)

最近のver.では修正済み

[1, 2, 3, 2, 3, 3, 3]
↓
{1: 1,  2: 1,  3: 1}

仕方ないので、イテレートしてinc(key)でそのキーのカウントを1増やしていく。(Pythonの defaultdict(int) みたいな使い方ができる)

let a = @[1, 2, 3, 2, 3, 3, 3]
var b = initCountTable[int]()
for v in a:
    b.inc(v)
注意点2

要素を多い順に並べ替えるsort()があるが、要素数が増えると妙に遅い。(シェルソートしてるらしいんだけど……)

ソートは自前実装した方がよい。

最も多い物が欲しいだけなら、largest()が使える。

version

現状のAtCoderのバージョン0.13では、正直コンテスト本番に使うのは難しい感触。(記述時の最新は0.19.0)

上記CountTableのようなバグが潜んでいたり、下記のようにうっかりミスしやすい処理の優先順位となっていたりする。

let n = 10
echo (n - 1) * 100

# このコードがコンパイルエラーになる

# Error: type mismatch: got (empty, int literal(100))
# but expected one of: 
# system.*(x: T, y: T)
# system.*(x: int16, y: int16)
# ...

# echo も関数なのでカッコが「echo(n - 1)」と先に評価されている
# 0.19では解消され、通る
# 0.13では明示的にカッコでくくってあげる必要がある

let n = 10
echo((n - 1) * 100)
programming/nim/programming_contest.txt · 最終更新: 2018/10/19 by ikatakos
CC Attribution 4.0 International
Driven by DokuWiki Recent changes RSS feed Valid CSS Valid XHTML 1.0