問題一覧 > 通常問題

No.2906 Strange Online Judge

レベル : / 実行時間制限 : 1ケース 2.000秒 / メモリ制限 : 512 MB / スペシャルジャッジ問題 (複数の解が存在する可能性があります)
タグ : / 解いたユーザー数 8
作問者 : 👑 獅子座じゃない人獅子座じゃない人 / テスター : 👑 amentorimaruamentorimaru 👑 MizarMizar cleanttedcleantted 👑 NachiaNachia
1 ProblemId : 10903 / 出題時の順位表 / 自分の提出
問題文最終更新日: 2024-09-28 13:46:06

問題文

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

問題

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

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

正の整数 KK が与えられたとき、数列 (an)(a_n) を次のように再帰的に定めます。

  • a1=1a_1=1
  • an+1=mex({0}{ai  1in}{ai+Ki  1in}) (n1)a_{n+1}=\operatorname{mex}(\{0\}\cup\{a_i\ |\ 1\leq i\leq n\}\cup\{a_i+Ki\ |\ 1\leq i\leq n\})\ (n\geq 1)

aNa_N を求めてください。

制約
  • 入力は全て整数
  • 1K1091\leq K\leq 10^9
  • 1N1091\leq N\leq 10^9

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

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

入力

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

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

出力

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

ジャッジについて

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

StLangの仕様

サンプルコード

StLang で 二項係数 NCK(N62){}_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 の主な仕様

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

    • 10進整数値、定数K、定数N、変数$0$9 以外のリテラル・定数・変数は利用できません。
    • 1000行を超えるプログラムを出力すると WA となります。1行に複数の文を含めることはできません。
    • 1ケースにおける代入操作と比較演算の合計実行回数 CC10001000 を超えると WA となります。
    • 10進整数値・算術演算(+,-,*,/,%)にて 00 未満 の値、 2642^{64} 以上 の値、 00 除算 が発生すると 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_n | variable
number: /[0-9]+/
const_k: "K"
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, 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), '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_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 によるインタプリタの実装例を併記します。ソースコード全体は 10001000 行以下の構文 program で記述されていなければなりません。ソースコードが以下のように再帰的に定義された、正しい構文で記述されていない場合、WAとなります。

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

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

    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), '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_n | variable
  • number: 【概要】10進整数値 【例外】値が 00 以上 2641=184467440737095516152^{64}-1=18446744073709551615 以下でなければ WA

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

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

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

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

    number: /[0-9]+/
    const_k: "K"
    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_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整数)を代入する

    代入が行われる毎に CC の値が 11 加算される【例外】CC の値が 10001000 より大きくなると 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+ba+b 【例外】結果の値が 00 以上 2641=184467440737095516152^{64}-1=18446744073709551615 以下でなければ WA

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

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

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

    rem: 【概要】剰余算 aba/ba-b\lfloor a/b\rfloor 【例外】除数が 00 だと 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 が評価される時に CC の値が 11 加算される 【例外】CC の値が 10001000 より大きくなると 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 が評価される毎に CC の値が 11 加算される 【例外】CC の値が 10001000 より大きくなると 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
  • 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"
    • "N"
    • variable
  • numberは、以下のものである。
    • "[0-9]+"
  • variableは、以下のものである。
    • "$" var_ident
  • var_identは、以下のものである。
    • "[0-9]"
  • lineendsは、以下のいずれかである。
    • "#" ".*"
    • lineend lineends
  • lineendは、以下のいずれかである。
    • "\n"
    • "#" ".*" "\n"
処理
(ここをクリックで開閉します)

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

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

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

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

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

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

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

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

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

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

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

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

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

assignmentは、variablevar_ident に対応する ViV_i の値をexpressionのもつ値に変更します。また、 CC の値が 11 大きくなります。

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

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

CC の値が 10001000 より大きくなった場合、WAとなります。

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)
        n = min(random.randrange(1, 2**random.randrange(1, 32)), 10**9)
        ### TODO: k,n の値から回答すべき値 an を生成するコードを書く
        an = 0
        return k, 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, n, an in itertools.chain([ # 本番問題用のサンプルケース
            ( 3, 5, 6 ),
            ( 8, 1, 1 ),
            ( 10, 1000000000, 1099019513 ),
        ], 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_n | variable
    number: /[0-9]+/
    const_k: "K"
    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), '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_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 アカウント)

サンプル

ジャッジが定数として与える KKNN と、返すべき値のサンプルです。「定数」の 22 つの数は順に KKNN です。

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

K=3, N=5K=3,\ N=5 です。

a1=1, a2=mex{0,1,4}=2, a3=mex{0,1,2,4,8}=3, a4=mex{0,1,2,3,4,8,12}=5, a5=mex{0,1,2,3,4,5,8,12,17}=6a_1=1,\ a_2=\operatorname{mex}\{0,1,4\}=2,\ a_3=\operatorname{mex}\{0,1,2,4,8\}=3,\ a_4=\operatorname{mex}\{0,1,2,3,4,8,12\}=5,\ a_5=\operatorname{mex}\{0,1,2,3,4,5,8,12,17\}=6 より、解は 66 です。

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

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

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