No.2906 Strange Online Judge
タグ : / 解いたユーザー数 8
作問者 : 👑
![amentorimaru](https://pbs.twimg.com/profile_images/1257015742903406592/16VDjMfb.jpg)
![cleantted](https://pbs.twimg.com/profile_images/1474872755321729026/6r0DKtiz.jpg)
![Nachia](https://pbs.twimg.com/profile_images/1774764955763654656/4aQlUed4.jpg)
問題文
あなたは、オンラインジャッジ「StrOnJ」を使用したプログラミングコンテストに参加しています。問題文は以下のようなものです。
問題
非負整数の集合 について、 を「 に含まれない最小の非負整数」とします。
厳密には、非負整数全体の集合 と非負整数の集合 について、 を で定義します。
正の整数 が与えられたとき、数列 を次のように再帰的に定めます。
を求めてください。
制約
- 入力は全て整数
ただし、StrOnJでは、この問題のために作られたプログラミング言語である「StLang」のソースコード以外提出することができません。
以下に示すStLangの仕様に従って、 を求めることができるStLangのソースコードを出力してください。ジャッジの都合上、出力の行数は1000行以下にしてください。
入力
問題としての入力は存在しません。
ジャッジでは、出力されたプログラムに対して を満たす整数 が与えられます。
出力
を求めることができるStLangのソースコードを出力してください。出力の行数は1000行以下にしてください。
ジャッジについて
提出されたプログラムの実行時エラーと区別するため、実行制限とメモリ制限を守って出力が行われた場合、ジャッジの結果はACまたはWAとなります。
StLangの仕様
サンプルコード
StLang で 二項係数 を計算するサンプルコードです。それぞれ、空白・コメント文字列を使用しない例・使用した例です。
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 では 以上 未満 の整数のみ扱うことができます。
- 変数は
$0
~$9
の10個のみ使えます。各テストケース毎の実行開始時、$0
~$9
の変数は で初期化されます。 - テストケースごとに与えられる定数、定数を値
K
,N
として使うことができます。 - 算術演算(
+
,-
,*
,/
,%
)は代入文の中で最大1回のみ扱えます。演算の結合はできません。 - 制御構文として使える
if
文,while
文 の条件式では、2つの値の比較演算(==
,!=
,>=
,<=
,>
,<
)のみ扱います。 #
文字以降行末までコメントとして扱われます。- 複数文字のキーワード(
if
,while
,end
,return
,==
,!=
,>=
,<=
)及び10進整数値の文字列の途中を除き、半角スペースは無視されます。
-
特筆すべき注意点
- 10進整数値、定数
K
、定数N
、変数$0
~$9
以外のリテラル・定数・変数は利用できません。 - 1000行を超えるプログラムを出力すると WA となります。1行に複数の文を含めることはできません。
- 1ケースにおける代入操作と比較演算の合計実行回数 が を超えると WA となります。
- 10進整数値・算術演算(
+
,-
,*
,/
,%
)にて 未満 の値、 以上 の値、 除算 が発生すると WA となります。 - 制御構文 (
if
,while
) ではend
にてブロック文の終了を対になるように明示することが必要です。 return
文 はプログラムの最後に1回だけ記述する必要があります。制御構文のブロック内にreturn
文 を含める事はできません。- 制御構文 (
if
,while
) における条件式 やreturn
文 の返り値 の中に算術演算(+
,-
,*
,/
,%
)を含める事はできません。
- 10進整数値、定数
構文・処理の詳細
(ここをクリックで開閉します)
入力されたプログラムを構文解析し、下図のような構文木を構築し(必ずしも実行に必要としないノードは構築を省略する場合あり)、入力ケースごとに変数・定数の初期値が設定され、この構文木を(制御構文を除き、基本的には)根から順番に辿るように実行します。
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 によるインタプリタの実装例を併記します。ソースコード全体は 行以下の構文 program
で記述されていなければなりません。ソースコードが以下のように再帰的に定義された、正しい構文で記述されていない場合、WAとなります。
プログラムを実行するコンピュータは、非負整数の列 および非負整数 をもつとします。
テストケース毎に、テストケースから与えられた の値が定数値K
,N
に設定され、変数 ~ ($0
~$9
) の値は に初期化され、実行ステップカウンター の値は に初期化されます。実行中に の値が より大きくなった場合、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進整数値 【例外】値が 以上 以下でなければ WAconst_k
: 【概要】定数 【処理】テストケース毎に決まった値を返すconst_n
: 【概要】定数 【処理】テストケース毎に決まった値を返すvariable
: 【概要】変数値【処理】変数 ~ のうち、var_ident
に対応する値を返すvar_ident
: 【概要】変数識別子【構文】1桁の整数【処理】 ~ の変数への対応を示す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整数)を代入する代入が行われる毎に の値が 加算される【例外】 の値が より大きくなると 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
: 【概要】加算 【例外】結果の値が 以上 以下でなければ WAsub
: 【概要】減算 【例外】結果の値が 以上 以下でなければ WAmul
: 【概要】乗算 【例外】結果の値が 以上 以下でなければ WAdiv
: 【概要】除算 【例外】除数が だと WArem
: 【概要】剰余算 【例外】除数が だと WAadd: 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
が評価される時に の値が 加算される 【例外】 の値が より大きくなると WAbranching: "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
が評価される毎に の値が 加算される 【例外】 の値が より大きくなると WArepeating: "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
を数値リテラルとして評価します。記述された数字の列を非負整数として解釈したとき 以上 以下でなければ、WAとなります。
プログラムを実行するコンピュータは、非負整数の列 および非負整数 をもつとします。初期値は全て です。
"K"
は の値、 "N"
は の値を表す定数です。これらの値は各ケースごとに決まります。
variable
について、 を数字としたとき、"$i"
は の値を表す変数です。
number
、"K"
、"N"
、variable
の値は、value
に引き継がれます。
値 をもつvalue
であるa
と値 をもつvalue
であるb
を考えます。a
operator
b
の形であるexpression
について、その値は
operator
が"+"
の場合、operator
が"-"
の場合、operator
が"*"
の場合、operator
が"/"
の場合、operator
が"%"
の場合、
また、a
comparator
b
の形であるcondition
は
comparator
が"=="
の場合、 であるかどうかcomparator
が"!="
の場合、 であるかどうかcomparator
が">="
の場合、 であるかどうかcomparator
が"<="
の場合、 であるかどうかcomparator
が">"
の場合、 であるかどうかcomparator
が"<"
の場合、 であるかどうか
プログラムはprogram
のstatements
で記述された処理を行った後に、value
として指定された値を返して終了します。返された値が与えられた に対する でない場合、WAとなります。
lineend
と endcomment
については、内容は無視され、何も処理を行いません。
statements
について、の形であれば何も処理を行いません。
statement
statements
の形であれば、statement
で記述された処理を行った後、statements
の処理に移ります。
assignment
は、variable
の var_ident
に対応する の値をexpression
のもつ値に変更します。また、 の値が 大きくなります。
"if"
condition
statements
"end"
の形のbranching
について、まずcondition
の値にかかわらず、 の値が 大きくなります。
次に、condition
の値が真である場合のみstatements
で記述された処理が行われます。
"while"
condition
statements
"end"
の形のbranching
について、condition
の値が真である限り、 の値を 大きくしてstatements
で記述された処理を行うことを繰り返します。
condition
の値が偽になった場合、 の値が 大きくなり、このbranching
の処理は終了します。
の値が より大きくなった場合、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 アカウント)
サンプル
ジャッジが定数として与える と と、返すべき値のサンプルです。「定数」の つの数は順に と です。
サンプル1
定数
3 5
返すべき値
6
です。
より、解は です。
サンプル2
定数
8 1
返すべき値
1
サンプル3
定数
10 1000000000
返すべき値
1099019513
提出するには、Twitter 、GitHub、 Googleもしくは右上の雲マークをクリックしてアカウントを作成してください。