問題一覧 > 通常問題

No.2908 Strange Online Judge (Extra)

レベル : / 実行時間制限 : 1ケース 2.000秒 / メモリ制限 : 512 MB / スペシャルジャッジ問題 (複数の解が存在する可能性があります)
タグ : / 解いたユーザー数 1
作問者 : 👑 MizarMizar / テスター : 👑 獅子座じゃない人獅子座じゃない人
0 ProblemId : 11319 / 自分の提出
問題文最終更新日: 2024-10-07 20:42:14

問題文

あなたは、オンラインジャッジ「StrOnJ」を使用したプログラミングコンテストに参加しています。問題文は以下のようなものです。

問題

非負整数の集合 $S$ について、 $\operatorname{mex}{S}$ を「 $S$ に含まれない最小の非負整数」とします。

厳密には、非負整数全体の集合 $\mathbb{N}$ と非負整数の集合 $S$ について、 $\operatorname{mex}{S}$ を $\operatorname{mex}{S}=\min(\mathbb{N}\setminus S)$ で定義します。

正の整数 $K$ と 非負整数 $M$ ( ただし $0\leq M\leq K$ ) が与えられたとき、数列 $(a_n)$ を次のように再帰的に定めます。

  • $a_1=1$
  • $a_{n+1}=\operatorname{mex}(\{0\}\cup\{a_i\ |\ 1\leq i\leq n\}\cup\{a_i+Ki-M+1\ |\ 1\leq i\leq n\})\ (n\geq 1)$

$a_N$ を求めてください。

制約
  • 入力は全て整数
  • $1\leq K\leq 10^9$
  • $0\leq M\leq K$
  • $1\leq N\leq 10^9$

ただし、StrOnJでは、この問題のために作られたプログラミング言語である「StLang」のソースコード以外提出することができません。

以下に示すStLangの仕様に従って、 $a_N$ を求めることができるStLangのソースコードを出力してください。ジャッジの都合上、出力の行数は1000行以下にしてください。

入力

問題としての入力は存在しません。

ジャッジでは、出力されたプログラムに対して $1\leq K\leq 10^9,\ 0\leq M\leq K,\ 1\leq N\leq 10^9$ を満たす整数 $K,M,N$ が与えられます。

出力

$a_N$ を求めることができるStLangのソースコードを出力してください。出力の行数は1000行以下にしてください。

ジャッジについて

提出されたプログラムの実行時エラーと区別するため、実行制限とメモリ制限を守って出力が行われた場合、ジャッジの結果はACまたはWAとなります。

StLangの仕様

サンプルコード

StLang で 二項係数 ${}_NC_K\quad(N\leq 62)$ を計算するサンプルコードです。それぞれ、空白・コメント文字列を使用しない例・使用した例です。

ifN>=K
$0=1
$1=1
$2=N
while$1<=K
$0=$0*$2
$0=$0/$1
$2=$2-1
$1=$1+1
end
end
return$0
# 二項係数 nCk の計算 (N > 62 の時は演算オーバーフローする可能性あり)
if N >= K         # N < K の時の出力は 0 とする
  $0 = 1
  $1 = 1
  $2 = N
  while $1 <= K   # for $1 in 1..=K
    $0 = $0 * $2  # $2 == N + 1 - $1
    $0 = $0 / $1  # ここで割り算の余りは生じない
    $2 = $2 - 1
    $1 = $1 + 1
  end
end
return $0

# return 行より後のコメント行も可

StLang のソースコードを直接書いた場合、 言語に Text (cat 8.3) を選択することで、そのまま提出することができます。

StLang 仕様の概要
  • StLang の主な仕様

    • 問題「Strange Online Judge」との仕様の相違は、定数$M$ を 値M として追加で扱えるようになった点です。
    • StLang では $0$ 以上 $2^{64}$ 未満 の整数のみ扱うことができます。
    • 変数は$0$9の10個のみ使えます。各テストケース毎の実行開始時、$0$9の変数は $0$ で初期化されます。
    • テストケースごとに与えられる定数$K$、定数$M$、定数$N$を値 K,M,Nとして使うことができます。
    • 算術演算(+,-,*,/,%)は代入文の中で最大1回のみ扱えます。演算の結合はできません。
    • 制御構文として使える if文, while文 の条件式では、2つの値の比較演算(==,!=,>=,<=,>,<)のみ扱います。
    • # 文字以降行末までコメントとして扱われます。
    • 複数文字のキーワード(if,while,end,return,==,!=,>=,<=)及び10進整数値の文字列の途中を除き、半角スペースは無視されます。
  • 特筆すべき注意点

    • 10進整数値、定数K、定数M、定数N、変数$0$9 以外のリテラル・定数・変数は利用できません。
    • 1000行を超えるプログラムを出力すると WA となります。1行に複数の文を含めることはできません。
    • 1ケースにおける代入操作と比較演算の合計実行回数 $C$ が $1000$ を超えると WA となります。
    • 10進整数値・算術演算(+,-,*,/,%)にて $0$ 未満 の値、 $2^{64}$ 以上 の値、 $0$ 除算 が発生すると WA となります。
    • 制御構文 (if,while) では end にてブロック文の終了を対になるように明示することが必要です。
    • return文 はプログラムの最後に1回だけ記述する必要があります。制御構文のブロック内に return文 を含める事はできません。
    • 制御構文 (if,while) における条件式 や return文 の返り値 の中に算術演算(+,-,*,/,%)を含める事はできません。
構文・処理の詳細
(ここをクリックで開閉します)

入力されたプログラムを構文解析し、下図のような構文木を構築し(必ずしも実行に必要としないノードは構築を省略する場合あり)、入力ケースごとに変数・定数の初期値が設定され、この構文木を(制御構文を除き、基本的には)根から順番に辿るように実行します。

StLang の実行環境サンプル(後述)は、 Lark を用いて実装しています。

Lark - a parsing toolkit for Python : https://github.com/lark-parser/lark

入力されたプログラムから構文木への変換は以下のようなルールで行われます。

EBNF(拡張バッカス・ナウア記法) + 正規表現 + α による(パーサジェネレータ Lark 向け)StLang構文定義:

?start: program
program: statements "return" value lineends
?value: number | const_k | const_m | const_n | variable
number: /[0-9]+/
const_k: "K"
const_m: "M"
const_n: "N"
variable: "$" var_ident
var_ident: /[0-9]/
?statements: statement*
?statement: assignment | branching | repeating | lineend
assignment: "$" var_ident "=" expression lineend
?expression: value | add | sub | mul | div | rem
add: value "+" value
sub: value "-" value
mul: value "*" value
div: value "/" value
rem: value "%" value
branching: "if" condition lineend statements "end" lineend
repeating: "while" condition lineend statements "end" lineend
?condition: eq | ne | ge | le | gt | lt
eq: value "==" value
ne: value "!=" value
ge: value ">=" value
le: value "<=" value
gt: value ">" value
lt: value "<" value
?lineends: lineend* ST_COMMENT?
?lineend: ST_COMMENT? CR? LF
ST_COMMENT: "#" /[^\n]*/
CR : /\r/
LF : /\n/
WS_INLINE: (" "|/\t/)+
%ignore WS_INLINE

構文木上でのプログラムの実行は、以下のような実装で行われます。

パーサジェネレータ Lark によるインタプリタ全体の実装例は、後の「StLang @ Lark 実行環境サンプル」を参照してください。

パーサジェネレータ Lark によるインタプリタの実装例:

class StLangInterpreter(lark.visitors.Interpreter):
    """StLang Interpreter"""
    def __init__(self, k=0, m=0, n=0) -> None:
        super().__init__()
        assert 0 <= int(k) < 2**64 and 0 <= int(m) < 2**64 and 0 <= int(n) < 2**64, "OutOfRangeNumber"
        self.env = { 'C': 0, 'K': int(k), 'M': int(m), 'N': int(n), '0': 0, '1': 0, '2': 0, '3': 0, '4': 0, '5': 0, '6': 0, '7': 0, '8': 0, '9': 0 }
    def program(self, tree) -> int:
        self.visit(tree.children[0]) # statements
        return self.visit(tree.children[1]) # value
    def number(self, tree) -> int:
        ret = int(tree.children[0].value)
        assert 0 <= ret < 2**64, "OutOfRangeNumber"
        return ret
    def const_k(self, _tree) -> int:
        return self.env['K']
    def const_m(self, _tree) -> int:
        return self.env['M']
    def const_n(self, _tree) -> int:
        return self.env['N']
    def variable(self, tree) -> int:
        return self.env[self.visit(tree.children[0])]
    def var_ident(self, tree) -> str:
        return str(tree.children[0].value)
    def assignment(self, tree) -> None:
        self.env['C'] += 1
        assert self.env['C'] <= 1000, "StepLimitExceed"
        self.env[self.visit(tree.children[0])] = self.visit(tree.children[1])
    def add(self, tree) -> int:
        ret = self.visit(tree.children[0]) + self.visit(tree.children[1])
        assert 0 <= ret < 2**64, "OutOfRangeNumber"
        return ret
    def sub(self, tree) -> int:
        ret = self.visit(tree.children[0]) - self.visit(tree.children[1])
        assert 0 <= ret < 2**64, "OutOfRangeNumber"
        return ret
    def mul(self, tree) -> int:
        ret = self.visit(tree.children[0]) * self.visit(tree.children[1])
        assert 0 <= ret < 2**64, "OutOfRangeNumber"
        return ret
    def div(self, tree) -> int:
        lhs, rhs = self.visit(tree.children[0]), self.visit(tree.children[1])
        assert 0 < rhs, "OutOfRangeNumber"
        return lhs // rhs
    def rem(self, tree) -> int:
        lhs, rhs = self.visit(tree.children[0]), self.visit(tree.children[1])
        assert 0 < rhs, "OutOfRangeNumber"
        return lhs % rhs
    def branching(self, tree) -> None:
        self.env['C'] += 1
        assert self.env['C'] <= 1000, "StepLimitExceed"
        if self.visit(tree.children[0]): # condition
            self.visit(tree.children[2]) # statements
    def repeating(self, tree) -> None:
        while True:
            self.env['C'] += 1
            assert self.env['C'] <= 1000, "StepLimitExceed"
            if not self.visit(tree.children[0]): # condition
                break
            self.visit(tree.children[2]) # statements
    def eq(self, tree) -> bool:
        return self.visit(tree.children[0]) == self.visit(tree.children[1])
    def ne(self, tree) -> bool:
        return self.visit(tree.children[0]) != self.visit(tree.children[1])
    def ge(self, tree) -> bool:
        return self.visit(tree.children[0]) >= self.visit(tree.children[1])
    def le(self, tree) -> bool:
        return self.visit(tree.children[0]) <= self.visit(tree.children[1])
    def gt(self, tree) -> bool:
        return self.visit(tree.children[0]) > self.visit(tree.children[1])
    def lt(self, tree) -> bool:
        return self.visit(tree.children[0]) < self.visit(tree.children[1])

StLang の構文と処理の解説と、 EBNF(拡張バッカス・ナウア記法) + 正規表現 を主に用いた構文定義、Lark によるインタプリタの実装例を併記します。 ソースコード全体は $1000$ 行以下の構文 program で記述されていなければなりません。 ソースコードが以下のように再帰的に定義された、正しい構文で記述されていない場合、WAとなります。

プログラムを実行するコンピュータは、非負整数の列 $(V_0,V_1,V_2,\ldots,V_9)$ および非負整数 $C$ をもつとします。

テストケース毎に、テストケースから与えられた $K,M,N$ の値が定数値K,M,Nに設定され、 変数 $V_0$~$V_9$ ($0$9) の値は $0$ に初期化され、実行ステップカウンター $C$ の値は $0$ に初期化されます。 実行中に $C$ の値が $1000$ より大きくなった場合、WAとなります。

    def __init__(self, k=0, m=0, n=0) -> None:
        super().__init__()
        assert 0 <= int(k) < 2**64 and 0 <= int(m) < 2**64 and 0 <= int(n) < 2**64, "OutOfRangeNumber"
        self.env = { 'C': 0, 'K': int(k), 'M': int(m), 'N': int(n), '0': 0, '1': 0, '2': 0, '3': 0, '4': 0, '5': 0, '6': 0, '7': 0, '8': 0, '9': 0 }

構文定義の行頭に ? が付いているのは、構文木上でノードの構成を省略するものに付けられたもので、ここでは、子要素の実装をそのまま用いるため、それ自体では直接の機能実装が行われないものに適用しています。

"" で囲まれている部分は、字句文字列である事を表す

// で囲まれている部分は、正規表現による文字列マッチングの指定

? を後に付けた場合、 0回~1回 繰り返されることを示す(つまり、省略可能である事を示す)

* を後に付けた場合、 0回以上 繰り返されることを示す(省略可能な繰り返し)

+ を後に付けた場合、 1回以上 繰り返されることを示す(繰り返し)

| で併記した場合、いずれか1つが選ばれること (choice) を示す

/[0-9]+/ の正規表現は、数字1文字以上の文字列に当てはまることを示す

/[0-9]/ の正規表現は、数字1文字の文字列に当てはまることを示す

"#" /[^\n]*/ は、 # 文字から始まる、改行文字を含まない任意長の文字列に当てはまることを示す

/\r/ の正規表現は、キャートリッジリターン制御文字に当てはまることを示す

/\n/ の正規表現は、改行文字に当てはまることを示す

(" "|/\t/)+ は、半角空白文字もしくは水平タブ文字1文字以上に当てはまることを示す

  • start: 【概要】構文木のroot nodeの指定 【処理】 program の実行

    ?start: program
  • program: 【概要】プログラム全体の定義 【処理】 statements を実行した後に value の値を出力して終了する

    program: statements "return" value lineends
        def program(self, tree) -> int:
            self.visit(tree.children[0]) # statements
            return self.visit(tree.children[1]) # value
    
  • value: 【概要】整数値 【構文】number, const_k, const_n, variable のいずれか 【処理】いずれか対応する値を返す

    ?value: number | const_k | const_m | const_n | variable
  • number: 【概要】10進整数値 【例外】値が $0$ 以上 $2^{64}-1=18446744073709551615$ 以下でなければ WA

    const_k: 【概要】定数 $K$ 【処理】テストケース毎に決まった値を返す

    const_m: 【概要】定数 $M$ 【処理】テストケース毎に決まった値を返す

    const_n: 【概要】定数 $N$ 【処理】テストケース毎に決まった値を返す

    variable: 【概要】変数値【処理】変数 $V_0$ ~ $V_9$ のうち、 var_ident に対応する値を返す

    var_ident: 【概要】変数識別子【構文】1桁の整数【処理】$V_0$ ~ $V_9$ の変数への対応を示す

    number: /[0-9]+/
    const_k: "K"
    const_m: "M"
    const_n: "N"
    variable: "$" var_ident
    var_ident: /[0-9]/
    
        def number(self, tree) -> int:
            ret = int(tree.children[0].value)
            assert 0 <= ret < 2**64, "OutOfRangeNumber"
            return ret
        def const_k(self, _tree) -> int:
            return self.env['K']
        def const_m(self, _tree) -> int:
            return self.env['M']
        def const_n(self, _tree) -> int:
            return self.env['N']
        def variable(self, tree) -> int:
            return self.env[self.visit(tree.children[0])]
        def var_ident(self, tree) -> str:
            return str(tree.children[0].value)
    
  • statements: 【概要】ブロック文【構文】0行以上の statement からなる 【処理】 含まれる statement ごとに順番に実行する

    ?statements: statement*
  • statement: 【概要】文【構文】assignment, branching, repeating, 空行(もしくはコメント行) のいずれかからなる 【処理】対応するものを実行する

    ?statement: assignment | branching | repeating | lineend
  • assignment: 【概要】代入

    【処理】変数 var_ident に、 expression を評価した値(符号なし64bit整数)を代入する

    代入が行われる毎に $C$ の値が $1$ 加算される【例外】$C$ の値が $1000$ より大きくなると WA

    assignment: "$" var_ident "=" expression lineend
        def assignment(self, tree) -> None:
            self.env['C'] += 1
            assert self.env['C'] <= 1000, "StepLimitExceed"
            self.env[self.visit(tree.children[0])] = self.visit(tree.children[1])
    
  • expression: 【概要】整数値の評価【処理】 value, add, sub, mul, div, rem のいずれか対応するものを評価した値を返す

    ?expression: value | add | sub | mul | div | rem
  • add: 【概要】加算 $a+b$ 【例外】結果の値が $0$ 以上 $2^{64}-1=18446744073709551615$ 以下でなければ WA

    sub: 【概要】減算 $a-b$ 【例外】結果の値が $0$ 以上 $2^{64}-1=18446744073709551615$ 以下でなければ WA

    mul: 【概要】乗算 $a\times b$ 【例外】結果の値が $0$ 以上 $2^{64}-1=18446744073709551615$ 以下でなければ WA

    div: 【概要】除算 $\lfloor a/b\rfloor$ 【例外】除数が $0$ だと WA

    rem: 【概要】剰余算 $a-b\lfloor a/b\rfloor$ 【例外】除数が $0$ だと WA

    add: value "+" value
    sub: value "-" value
    mul: value "*" value
    div: value "/" value
    rem: value "%" value
    
        def add(self, tree) -> int:
            ret = self.visit(tree.children[0]) + self.visit(tree.children[1])
            assert 0 <= ret < 2**64, "OutOfRangeNumber"
            return ret
        def sub(self, tree) -> int:
            ret = self.visit(tree.children[0]) - self.visit(tree.children[1])
            assert 0 <= ret < 2**64, "OutOfRangeNumber"
            return ret
        def mul(self, tree) -> int:
            ret = self.visit(tree.children[0]) * self.visit(tree.children[1])
            assert 0 <= ret < 2**64, "OutOfRangeNumber"
            return ret
        def div(self, tree) -> int:
            lhs, rhs = self.visit(tree.children[0]), self.visit(tree.children[1])
            assert 0 < rhs, "OutOfRangeNumber"
            return lhs // rhs
        def rem(self, tree) -> int:
            lhs, rhs = self.visit(tree.children[0]), self.visit(tree.children[1])
            assert 0 < rhs, "OutOfRangeNumber"
            return lhs % rhs
    
  • branching: 【概要】分岐(if)

    【処理】condition の評価(bool) が true であれば statements を実行する

    condition が評価される時に $C$ の値が $1$ 加算される 【例外】$C$ の値が $1000$ より大きくなると WA

    branching: "if" condition lineend statements "end" lineend
        def branching(self, tree) -> None:
            self.env['C'] += 1
            assert self.env['C'] <= 1000, "StepLimitExceed"
            if self.visit(tree.children[0]): # condition
                self.visit(tree.children[2]) # statements
    
  • repeating: 【概要】繰り返し(while)

    【処理】condition の評価(bool) が true である間 statements を繰り返し実行する

    condition が評価される毎に $C$ の値が $1$ 加算される 【例外】$C$ の値が $1000$ より大きくなると WA

    repeating: "while" condition lineend statements "end" lineend
        def repeating(self, tree) -> None:
            while True:
                self.env['C'] += 1
                assert self.env['C'] <= 1000, "StepLimitExceed"
                if not self.visit(tree.children[0]): # condition
                    break
                self.visit(tree.children[2]) # statements
    
  • condition: 【概要】比較演算 【処理】eq, ne, ge, le, gt, lt のいずれか対応するものの評価(bool)を返す

    ?condition: eq | ne | ge | le | gt | lt
  • eq: 【概要】EQual 【処理】2つの value の値が等しいなら true、さもなくば false

    ne: 【概要】Not Equal 【処理】2つの value の値が異なるなら true、さもなくば false

    ge: 【概要】Greater than or Equal to 【処理】1番目のvalue は 2番目のvalue 以上なら true、さもなくば false

    le: 【概要】Less than or Equal to 【処理】1番目のvalue は 2番目のvalue 以下なら true、さもなくば false

    gt: 【概要】Greater Than 【処理】1番目のvalue は 2番目のvalue より 大なり なら true、さもなくば false

    lt: 【概要】Less Than 【処理】1番目のvalue は 2番目のvalue より 小なり なら true、さもなくば false

    eq: value "==" value
    ne: value "!=" value
    ge: value ">=" value
    le: value "<=" value
    gt: value ">" value
    lt: value "<" value
    
        def eq(self, tree) -> bool:
            return self.visit(tree.children[0]) == self.visit(tree.children[1])
        def ne(self, tree) -> bool:
            return self.visit(tree.children[0]) != self.visit(tree.children[1])
        def ge(self, tree) -> bool:
            return self.visit(tree.children[0]) >= self.visit(tree.children[1])
        def le(self, tree) -> bool:
            return self.visit(tree.children[0]) <= self.visit(tree.children[1])
        def gt(self, tree) -> bool:
            return self.visit(tree.children[0]) > self.visit(tree.children[1])
        def lt(self, tree) -> bool:
            return self.visit(tree.children[0]) < self.visit(tree.children[1])
    
  • lineends: 【概要】return 文より後の program 末尾【構文】0回以上の lineend、 1回以下の改行なしコメント文字列 【処理】何の処理も行わない

    ?lineends: lineend* ST_COMMENT?
  • lineend: 【概要】行末(コメント文字列が含まれる場合あり) 【構文】ST_COMMENT, CR は省略可【処理】何の処理も行わない

    ?lineend: ST_COMMENT? CR? LF
  • ST_COMMENT: 【概要】コメント文字列【構文】"#" 文字以降、改行文字を除く行末までをコメント文字列と見なす【処理】何の処理も行わない

    ST_COMMENT: "#" /[^\n]*/
  • CR: 【概要】キャートリッジリターン制御文字【処理】何の処理も行わない

    LF: 【概要】改行文字【処理】何の処理も行わない

    CR : /\r/
    LF : /\n/
    
  • WS_INLINE: 【概要】行内の空白文字類【構文】半角空白、水平タブ制御文字 のいずれか【処理】何の処理も行わない

    WS_INLINE: (" "|/\t/)+
  • 【構文】行内の空白文字類を無視

    %ignore WS_INLINE
字句
(ここをクリックで開閉します)

以下の字句を使用することができます。ただし、[0-9]は任意の1文字の数字を、[0-9]+は1文字以上の任意の数字の列を、.*は0文字以上の改行を除く任意の文字の列を、\nは改行を表します。

  • [0-9]+
  • K
  • M
  • N
  • $
  • [0-9]
  • +
  • -
  • *
  • /
  • %
  • if
  • while
  • end
  • ==
  • !=
  • >=
  • <=
  • >
  • <
  • =
  • return
  • #
  • .*
  • \n

また、半角スペース( )は無視されます。改行は無視されないことに注意してください。

構文
(ここをクリックで開閉します)

構文は以下のように再帰的に定義されます。ソースコード全体は構文programで記述されていなければなりません。

上で定義した字句はダブルクォーテーションで囲んで表すこととし、字句または構文の結合を空白区切りで表すものとします。また、は空文字列であるとします。

  • programは、以下のように定義される。
    • statements "return" value lineends
  • statementsは、以下のいずれかである。
    • statement statements
  • statementは、以下のいずれかである。
    • lineend
    • assignment
    • branching
  • assignmentは、以下のように定義される。
    • "$" "[0-9]" "=" expression lineend
  • branchingは、以下のように定義される。
    • control condition lineend statements "end" lineend
  • controlは、以下のいずれかである。
    • "if"
    • "while"
  • expressionは、以下のいずれかである。
    • value
    • value "+" value
    • value "-" value
    • value "*" value
    • value "/" value
    • value "%" value
  • conditionは、以下のいずれかである。
    • value "==" value
    • value "!=" value
    • value ">=" value
    • value "<=" value
    • value ">" value
    • value "<" value
  • valueは、以下のいずれかである。
    • number
    • "K"
    • "M"
    • "N"
    • variable
  • numberは、以下のものである。
    • "[0-9]+"
  • variableは、以下のものである。
    • "$" var_ident
  • var_identは、以下のものである。
    • "[0-9]"
  • lineendsは、以下のいずれかである。
    • "#" ".*"
    • lineend lineends
  • lineendは、以下のいずれかである。
    • "\n"
    • "#" ".*" "\n"
処理
(ここをクリックで開閉します)

ソースコードが正しい構文で記述されていない場合、WAとなります。

次に、numberを数値リテラルとして評価します。記述された数字の列を非負整数として解釈したとき $0$ 以上 $2^{64}-1=18446744073709551615$ 以下でなければ、WAとなります。

プログラムを実行するコンピュータは、非負整数の列 $(V_0,V_1,V_2,\ldots,V_9)$ および非負整数 $C$ をもつとします。初期値は全て $0$ です。

"K"は $K$ の値、 "M"は $M$ の値、 "N"は $N$ の値を表す定数です。これらの値は各ケースごとに決まります。

variableについて、 $i$ を数字としたとき、"$i"は $V_i$ の値を表す変数です。

number"K""M""N"variableの値は、valueに引き継がれます。

値 $a$ をもつvalueであるaと値 $b$ をもつvalueであるbを考えます。a operator bの形であるexpressionについて、その値は

  • operator"+"の場合、 $a+b$
  • operator"-"の場合、 $a-b$
  • operator"*"の場合、 $a \times b$
  • operator"/"の場合、 $\lfloor a / b\rfloor$
  • operator"%"の場合、 $a-b\lfloor a / b\rfloor$
となります。値が $0$ 以上 $2^{64}-1=18446744073709551615$ 以下でなければ、WAとなります。

また、a comparator bの形であるcondition

  • comparator"=="の場合、 $a=b$ であるかどうか
  • comparator"!="の場合、 $a\neq b$ であるかどうか
  • comparator">="の場合、 $a\geq b$ であるかどうか
  • comparator"<="の場合、 $a\leq b$ であるかどうか
  • comparator">"の場合、 $a\gt b$ であるかどうか
  • comparator"<"の場合、 $a\lt b$ であるかどうか
をもちます。

プログラムはprogramstatementsで記述された処理を行った後に、valueとして指定された値を返して終了します。返された値が与えられた $K,M,N$ に対する $a_N$ でない場合、WAとなります。

lineendendcomment については、内容は無視され、何も処理を行いません。

statementsについて、の形であれば何も処理を行いません。statement statementsの形であれば、statementで記述された処理を行った後、statementsの処理に移ります。

assignmentは、variablevar_ident に対応する $V_i$ の値をexpressionのもつ値に変更します。また、 $C$ の値が $1$ 大きくなります。

"if" condition statements "end"の形のbranchingについて、まずconditionの値にかかわらず、 $C$ の値が $1$ 大きくなります。 次に、conditionの値が真である場合のみstatementsで記述された処理が行われます。

"while" condition statements "end"の形のbranchingについて、conditionの値が真である限り、 $C$ の値を $1$ 大きくしてstatementsで記述された処理を行うことを繰り返します。 conditionの値が偽になった場合、 $C$ の値が $1$ 大きくなり、このbranchingの処理は終了します。

$C$ の値が $1000$ より大きくなった場合、WAとなります。( $1000$ で妥当であるかは要検証)

StLang @ Lark 実行環境サンプル

デフォルトでは StLang のサンプルコード が設定されているので、問題文に合ったプログラムに適宜書き換えてお使いください。

  • Pythonコード(簡易版) (ここをクリックで開閉します)
    """StLang@Lark 実行環境サンプル 簡易版"""
    """こちらは Lark モジュールのインストールが必要です"""
    """ Lark - a parsing toolkit for Python : https://github.com/lark-parser/lark """
    #%pip install lark>=1.1.9 --upgrade
    import lark
    
    ### TODO: ここからを本番の問題用の正しいコードに書き換える
    PROGRAM = r"""
    # サンプルプログラム: 二項係数 nCk の計算 (N > 62 の時は演算オーバーフローする可能性あり)
    if N >= K         # N < K の時の出力は 0 とする
      $0 = 1
      $1 = 1
      $2 = N
      while $1 <= K   # for $1 in 1..=K
        $0 = $0 * $2  # $2 == N + 1 - $1
        $0 = $0 / $1  # ここで割り算の余りは生じない
        $2 = $2 - 1
        $1 = $1 + 1
      end
    end
    return $0
    
    # return 行より後のコメント行も可
    """
    ### TODO: ここまでを本番の問題用の正しいコードに書き換える
    
    import itertools
    import random
    import sys
    
    """ランダムテストケースの生成"""
    def gen_test(_: int) -> tuple[int, int, int]:
        k = min(random.randrange(1, 2**random.randrange(1, 32)), 10**9)
        m = random.randrange(0, k+1)
        n = min(random.randrange(1, 2**random.randrange(1, 32)), 10**9)
        ### TODO: k,m,n の値から回答すべき値 an を生成するコードを書く
        an = 0
        return k, m, n, an
    
    ### TODO: ランダムテストケースの生成を正しく書いた後、ランダムテストケース生成数を書き換える
    """ランダムテストケースの生成数"""
    GEN_CASES = 0
    
    def main() -> None:
        stlang_parser = lark.Lark(
            STLANG_GRAMMER,
            parser='lalr',
        )
        assert len(PROGRAM.splitlines()) <= 1000
        stlang_tree = stlang_parser.parse(PROGRAM)
        for k, m, n, an in itertools.chain([ # 本番問題用のサンプルケース
            ( 3, 1, 5, 6 ),
            ( 8, 1, 1, 1 ),
            ( 10, 1, 1000000000, 1099019513 ),
            ( 3, 0, 5, 6 ),
            ( 8, 8, 1, 1 ),
            ( 10, 6, 1000000000, 1099019514 ),
        ], map(gen_test, range(GEN_CASES))):
            assert 1 <= k <= 10**9 and 1 <= n <= 10**9 and 0 <= an < 2**64
            result, judge, worker = None, None, StLangInterpreter(k, n)
            try:
                result = worker.visit(stlang_tree)
                judge = "Accepted" if result == an else "WrongAnswer"
            except AssertionError as e:
                judge, tb = "RuntimeError", e.__traceback__
                print(type(e), e, file=sys.stderr)
                while tb is not None:
                    print(tb.tb_frame, file=sys.stderr)
                    tb = tb.tb_next
            print(f"{judge}: K={k} N={n} a_N={an} prog_return:{result} C:{worker.env['C']}")
    
    STLANG_GRAMMER = r"""
    ?start: program
    program: statements "return" value lineends
    ?value: number | const_k | const_m | const_n | variable
    number: /[0-9]+/
    const_k: "K"
    const_m: "M"
    const_n: "N"
    variable: "$" var_ident
    var_ident: /[0-9]/
    ?statements: statement*
    ?statement: assignment | branching | repeating | lineend
    assignment: "$" var_ident "=" expression lineend
    ?expression: value | add | sub | mul | div | rem
    add: value "+" value
    sub: value "-" value
    mul: value "*" value
    div: value "/" value
    rem: value "%" value
    branching: "if" condition lineend statements "end" lineend
    repeating: "while" condition lineend statements "end" lineend
    ?condition: eq | ne | ge | le | gt | lt
    eq: value "==" value
    ne: value "!=" value
    ge: value ">=" value
    le: value "<=" value
    gt: value ">" value
    lt: value "<" value
    ?lineends: lineend* ST_COMMENT?
    ?lineend: ST_COMMENT? CR? LF
    ST_COMMENT: "#" /[^\n]*/
    CR : /\r/
    LF : /\n/
    WS_INLINE: (" "|/\t/)+
    %ignore WS_INLINE
    """
    
    class StLangInterpreter(lark.visitors.Interpreter):
        """StLang Interpreter"""
        def __init__(self, k=0, n=0) -> None:
            super().__init__()
            assert 0 <= int(k) < 2**64 and 0 <= int(n) < 2**64, "OutOfRangeNumber"
            self.env = { 'C': 0, 'K': int(k), 'M': int(m), 'N': int(n), '0': 0, '1': 0, '2': 0, '3': 0, '4': 0, '5': 0, '6': 0, '7': 0, '8': 0, '9': 0 }
        def program(self, tree) -> int:
            self.visit(tree.children[0])
            return self.visit(tree.children[1])
        def number(self, tree) -> int:
            ret = int(tree.children[0].value)
            assert 0 <= ret < 2**64, "OutOfRangeNumber"
            return ret
        def const_k(self, _tree) -> int:
            return self.env['K']
        def const_m(self, _tree) -> int:
            return self.env['M']
        def const_n(self, _tree) -> int:
            return self.env['N']
        def variable(self, tree) -> int:
            return self.env[self.visit(tree.children[0])]
        def var_ident(self, tree) -> str:
            return str(tree.children[0].value)
        def assignment(self, tree) -> None:
            self.env['C'] += 1
            assert self.env['C'] <= 1000, "StepLimitExceed"
            self.env[self.visit(tree.children[0])] = self.visit(tree.children[1])
        def add(self, tree) -> int:
            ret = self.visit(tree.children[0]) + self.visit(tree.children[1])
            assert 0 <= ret < 2**64, "OutOfRangeNumber"
            return ret
        def sub(self, tree) -> int:
            ret = self.visit(tree.children[0]) - self.visit(tree.children[1])
            assert 0 <= ret < 2**64, "OutOfRangeNumber"
            return ret
        def mul(self, tree) -> int:
            ret = self.visit(tree.children[0]) * self.visit(tree.children[1])
            assert 0 <= ret < 2**64, "OutOfRangeNumber"
            return ret
        def div(self, tree) -> int:
            lhs, rhs = self.visit(tree.children[0]), self.visit(tree.children[1])
            assert 0 < rhs, "OutOfRangeNumber"
            return lhs // rhs
        def rem(self, tree) -> int:
            lhs, rhs = self.visit(tree.children[0]), self.visit(tree.children[1])
            assert 0 < rhs, "OutOfRangeNumber"
            return lhs % rhs
        def branching(self, tree) -> None:
            self.env['C'] += 1
            assert self.env['C'] <= 1000, "StepLimitExceed"
            if self.visit(tree.children[0]): # condition
                self.visit(tree.children[2]) # statements
        def repeating(self, tree) -> None:
            while True:
                self.env['C'] += 1
                assert self.env['C'] <= 1000, "StepLimitExceed"
                if not self.visit(tree.children[0]): # condition
                    break
                self.visit(tree.children[2]) # statements
        def eq(self, tree) -> bool:
            return self.visit(tree.children[0]) == self.visit(tree.children[1])
        def ne(self, tree) -> bool:
            return self.visit(tree.children[0]) != self.visit(tree.children[1])
        def ge(self, tree) -> bool:
            return self.visit(tree.children[0]) >= self.visit(tree.children[1])
        def le(self, tree) -> bool:
            return self.visit(tree.children[0]) <= self.visit(tree.children[1])
        def gt(self, tree) -> bool:
            return self.visit(tree.children[0]) > self.visit(tree.children[1])
        def lt(self, tree) -> bool:
            return self.visit(tree.children[0]) < self.visit(tree.children[1])
    
    main()
    
  • ソースコード: Python (スタンドアロン版、Larkインストール不要) ソースコードをコピーして yukicoderオンライン実行 上で実行可能です
  • ソースコード: Jupyter Notebook (VSCode + Jupyter Notebook拡張など、ローカル環境向け)
  • オンライン実行環境: Google Colab (要 Google アカウント)

サンプル

ジャッジが定数として与える $K\ M\ N$ と、返すべき値のサンプルです。「定数」の $3$ つの数は順に $K\ M\ N$ です。

サンプル1
定数
3 0 5
返すべき値
6

$K=3,\ M=0,\ N=5$ です。

$a_1=1,\ a_2=\operatorname{mex}\{0,1,5\}=2,\ a_3=\operatorname{mex}\{0,1,2,5,9\}=3,\ a_4=\operatorname{mex}\{0,1,2,3,5,9,13\}=4,\ a_5=\operatorname{mex}\{0,1,2,3,4,5,9,13,17\}=6$ より、解は $6$ です。

サンプル2
定数
8 8 1
返すべき値
1

サンプル3
定数
10 6 1000000000
返すべき値
1099019514

提出するには、Twitter 、GitHub、 Googleもしくは右上の雲マークをクリックしてアカウントを作成してください。