使う数字が b 個なら、番号札は b 進法の数の各桁の値を、使う数字に置き換えて書いたものです。 4 だけを除くと 9 進法で、桁の値 0 ~ 8 が数字 0, 1, 2, 3, 5, 6, 7, 8, 9 になります。 1つ目の例の L 50 は桁の値が 4, 0 なので 4 × 9 + 0 = 36 です。 罠その1: 0 を除いたなら、ふつうの b 進法ではありません。 桁の値が 1 ~ b の「0 のない b 進法」なので、余りが 0 のときは桁の値 b を使い、k から b を引いてから割ります。 上の桁から1つ借りることになります。 1 と 2 だけを使う2つ目の例で、ふつうの 2 進法に直すと I 2 が 2 ではなく 21 になり、借りずにそのまま割ると 12 になります。 罠その2: 余りは下の桁から出てきます。 逆にしないと、1つ目の例の I 9 が 10 ではなく 01 になります。 罠その3: 番号札は 1 からです。 0 を使う建物でも 0 は番号札ではないので、k をそのまま b 進法に直します。 k - 1 を直すと、1つ目の例の I 4 が 5 ではなく 3 になります。 罠その4: 番号札は 50 桁まであるので、数として読んではいけません。 JS の Number(x) は 16 桁あたりから下の桁を失い、2つ目の例の最後の行が違ってしまいます。 解答は8つの言語すべてで番号札を文字列として読み、1文字ずつ見ます。 罠その5: 順番は 10^15 まであるので、32 ビットの int に入れるとあふれます。 Java で int にためると、2つ目の例の最後の行が -1530494977 になります。 解答は C・C++ で long long、Java・C# で long を使います。 JS・TS の number とクレイコンの数は 2^53 まで正確なので 10^15 は大丈夫で、Python の整数には大きさの制限がありません。 商は Python k // b、JS・TS Math.floor(k / b)、Java・C#・C・C++ は整数どうしの k / b、クレイコンは 数学.商(k, b) です。 数字が何番目の桁の値かは、Python allowed.index(ch)、JS・TS・Java indexOf、C# IndexOf、C strchr、C++ find、クレイコン 位置検索 で探します。
Pythonmain.py · 45 行
import sysdef main(): lines = sys.stdin.read().split("\n") banned = lines[0].strip() allowed = [d for d in "0123456789" if d not in banned] # 쓰는 숫자, 작은 것부터 b = len(allowed) has_zero = allowed[0] == "0" def to_label(k): digits = [] while k > 0: r = k % b if has_zero: # 보통의 b진법: 나머지 0 ~ b-1이 그대로 자릿값이다 digits.append(allowed[r]) k //= b else: # 0이 없는 b진법: 자릿값은 1 ~ b. 나머지 0은 자릿값 b로 치고 윗자리에서 하나를 빌린다 if r == 0: r = b digits.append(allowed[r - 1]) k = (k - r) // b return "".join(reversed(digits)) # 낮은 자리부터 나왔으니 뒤집는다 def to_index(label): k = 0 for ch in label: value = allowed.index(ch) if has_zero else allowed.index(ch) + 1 k = k * b + value return k q = int(lines[1]) out = [] for line in lines[2:2 + q]: kind, arg = line.split() if kind == "I": out.append(to_label(int(arg))) else: out.append(str(to_index(arg))) # 번호표는 수로 바꾸지 않고 문자열 그대로 본다 print("\n".join(out))main()
JavaScriptmain.js · 40 行
const lines = require("fs").readFileSync(0, "utf8").split("\n");const banned = lines[0].trim();const allowed = [..."0123456789"].filter((d) => !banned.includes(d)); // 쓰는 숫자, 작은 것부터const b = allowed.length;const hasZero = allowed[0] === "0";function toLabel(k) { const digits = []; while (k > 0) { let r = k % b; if (hasZero) { // 보통의 b진법 digits.push(allowed[r]); k = Math.floor(k / b); } else { // 0이 없는 b진법: 나머지 0은 자릿값 b로 치고 윗자리에서 하나를 빌린다 if (r === 0) r = b; digits.push(allowed[r - 1]); k = (k - r) / b; } } return digits.reverse().join("");}function toIndex(label) { let k = 0; for (const ch of label) { k = k * b + allowed.indexOf(ch) + (hasZero ? 0 : 1); } return k;}const q = Number(lines[1]);const out = [];for (let i = 2; i < 2 + q; i++) { const [kind, arg] = lines[i].split(" "); // 차례 번호는 10^15 이하라 Number로 정확하다. 번호표는 50자리까지라 문자열 그대로 본다 out.push(kind === "I" ? toLabel(Number(arg)) : String(toIndex(arg)));}console.log(out.join("\n"));
TypeScriptmain.ts · 40 行
const lines: string[] = require("fs").readFileSync(0, "utf8").split("\n");const banned: string = lines[0].trim();const allowed: string[] = [..."0123456789"].filter((d) => !banned.includes(d)); // 쓰는 숫자, 작은 것부터const b: number = allowed.length;const hasZero: boolean = allowed[0] === "0";function toLabel(k: number): string { const digits: string[] = []; while (k > 0) { let r = k % b; if (hasZero) { // 보통의 b진법 digits.push(allowed[r]); k = Math.floor(k / b); } else { // 0이 없는 b진법: 나머지 0은 자릿값 b로 치고 윗자리에서 하나를 빌린다 if (r === 0) r = b; digits.push(allowed[r - 1]); k = (k - r) / b; } } return digits.reverse().join("");}function toIndex(label: string): number { let k = 0; for (const ch of label) { k = k * b + allowed.indexOf(ch) + (hasZero ? 0 : 1); } return k;}const q: number = Number(lines[1]);const out: string[] = [];for (let i = 2; i < 2 + q; i++) { const [kind, arg] = lines[i].split(" "); // 차례 번호는 10^15 이하라 number로 정확하다. 번호표는 50자리까지라 문자열 그대로 본다 out.push(kind === "I" ? toLabel(Number(arg)) : String(toIndex(arg)));}console.log(out.join("\n"));
JavaMain.java · 58 行
import java.io.BufferedReader;import java.io.IOException;import java.io.InputStreamReader;public class Main { static String allowed; // 쓰는 숫자, 작은 것부터 static int b; static boolean hasZero; static String toLabel(long k) { StringBuilder sb = new StringBuilder(); while (k > 0) { int r = (int) (k % b); if (hasZero) { // 보통의 b진법 sb.append(allowed.charAt(r)); k /= b; } else { // 0이 없는 b진법: 나머지 0은 자릿값 b로 치고 윗자리에서 하나를 빌린다 if (r == 0) r = b; sb.append(allowed.charAt(r - 1)); k = (k - r) / b; } } return sb.reverse().toString(); } static long toIndex(String label) { long k = 0; // 10^15까지라 int로는 모자란다 for (int i = 0; i < label.length(); i++) { k = k * b + allowed.indexOf(label.charAt(i)) + (hasZero ? 0 : 1); } return k; } public static void main(String[] args) throws IOException { BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); String banned = br.readLine().trim(); StringBuilder a = new StringBuilder(); for (char d = '0'; d <= '9'; d++) { if (banned.indexOf(d) < 0) a.append(d); } allowed = a.toString(); b = allowed.length(); hasZero = allowed.charAt(0) == '0'; int q = Integer.parseInt(br.readLine().trim()); StringBuilder out = new StringBuilder(); for (int i = 0; i < q; i++) { String[] parts = br.readLine().split(" "); if (parts[0].equals("I")) { out.append(toLabel(Long.parseLong(parts[1]))).append('\n'); } else { out.append(toIndex(parts[1])).append('\n'); } } System.out.print(out); }}
C#Main.cs · 64 行
using System;using System.Text;class Program{ static string allowed = ""; // 쓰는 숫자, 작은 것부터 static int b; static bool hasZero; static string ToLabel(long k) { var digits = new StringBuilder(); while (k > 0) { int r = (int)(k % b); if (hasZero) { // 보통의 b진법 digits.Insert(0, allowed[r]); k /= b; } else { // 0이 없는 b진법: 나머지 0은 자릿값 b로 치고 윗자리에서 하나를 빌린다 if (r == 0) r = b; digits.Insert(0, allowed[r - 1]); k = (k - r) / b; } } return digits.ToString(); } static long ToIndex(string label) { long k = 0; // 10^15까지라 int로는 모자란다 foreach (char ch in label) { k = k * b + allowed.IndexOf(ch) + (hasZero ? 0 : 1); } return k; } static void Main() { string banned = Console.ReadLine().Trim(); var a = new StringBuilder(); for (char d = '0'; d <= '9'; d++) { if (banned.IndexOf(d) < 0) a.Append(d); } allowed = a.ToString(); b = allowed.Length; hasZero = allowed[0] == '0'; int q = int.Parse(Console.ReadLine()); var sb = new StringBuilder(); for (int i = 0; i < q; i++) { string[] parts = Console.ReadLine().Split(' '); if (parts[0] == "I") sb.Append(ToLabel(long.Parse(parts[1]))).Append('\n'); else sb.Append(ToIndex(parts[1])).Append('\n'); } Console.Write(sb); }}
C++main.cpp · 57 行
#include <algorithm>#include <iostream>#include <string>using namespace std;string allowed; // 쓰는 숫자, 작은 것부터int b;bool hasZero;string toLabel(long long k) { string digits; while (k > 0) { int r = static_cast<int>(k % b); if (hasZero) { // 보통의 b진법 digits += allowed[r]; k /= b; } else { // 0이 없는 b진법: 나머지 0은 자릿값 b로 치고 윗자리에서 하나를 빌린다 if (r == 0) r = b; digits += allowed[r - 1]; k = (k - r) / b; } } reverse(digits.begin(), digits.end()); return digits;}long long toIndex(const string& label) { long long k = 0; // 10^15까지라 int로는 모자란다 for (char ch : label) { k = k * b + static_cast<long long>(allowed.find(ch)) + (hasZero ? 0 : 1); } return k;}int main() { ios::sync_with_stdio(false); cin.tie(nullptr); string banned; cin >> banned; for (char d = '0'; d <= '9'; d++) { if (banned.find(d) == string::npos) allowed += d; } b = static_cast<int>(allowed.size()); hasZero = allowed[0] == '0'; int q; cin >> q; for (int i = 0; i < q; i++) { char kind; string arg; // 번호표는 50자리까지라 수가 아닌 문자열로 읽는다 cin >> kind >> arg; if (kind == 'I') cout << toLabel(stoll(arg)) << '\n'; else cout << toIndex(arg) << '\n'; } return 0;}
Cmain.c · 63 行
#include <stdio.h>#include <string.h>static char allowed[11]; /* 쓰는 숫자, 작은 것부터 */static int b;static int has_zero;/* k번째 번호표를 out에 적는다 */static void to_label(long long k, char *out) { char rev[64]; int len = 0; while (k > 0) { int r = (int)(k % b); if (has_zero) { /* 보통의 b진법 */ rev[len++] = allowed[r]; k /= b; } else { /* 0이 없는 b진법: 나머지 0은 자릿값 b로 치고 윗자리에서 하나를 빌린다 */ if (r == 0) r = b; rev[len++] = allowed[r - 1]; k = (k - r) / b; } } for (int i = 0; i < len; i++) out[i] = rev[len - 1 - i]; out[len] = '\0';}static long long to_index(const char *label) { long long k = 0; /* 10^15까지라 int로는 모자란다 */ for (int i = 0; label[i] != '\0'; i++) { int value = (int)(strchr(allowed, label[i]) - allowed); k = k * b + value + (has_zero ? 0 : 1); } return k;}int main(void) { char banned[16]; if (scanf("%15s", banned) != 1) return 0; for (char d = '0'; d <= '9'; d++) { if (strchr(banned, d) == NULL) allowed[b++] = d; } allowed[b] = '\0'; has_zero = allowed[0] == '0'; int q; if (scanf("%d", &q) != 1) return 0; for (int i = 0; i < q; i++) { char kind; char arg[64]; /* 번호표는 50자리까지라 수가 아닌 문자열로 읽는다 */ if (scanf(" %c %63s", &kind, arg) != 2) return 0; if (kind == 'I') { long long k; char label[64]; sscanf(arg, "%lld", &k); to_label(k, label); printf("%s\n", label); } else { printf("%lld\n", to_index(arg)); } } return 0;}
Craykonmain.cray · 68 行
뺀숫자 = コンソール.行読み().空白除去();// 쓰는 숫자를 작은 것부터 이어 붙인 문자열쓰는숫자 = "";繰り返す (d = 0; d <= 9; d++){ もし (!뺀숫자.含む(d + "")) { 쓰는숫자 = 쓰는숫자 + d; }}b = 쓰는숫자.長さ;영있나 = 쓰는숫자.切り出す(0, 1) == "0";번호표로(k){ 결과 = ""; 間 (k > 0) { r = k % b; もし (영있나) { // 보통의 b진법 결과 = 쓰는숫자.切り出す(r, 1) + 결과; k = 数学.商(k, b); } でなければ { // 0이 없는 b진법: 나머지 0은 자릿값 b로 치고 윗자리에서 하나를 빌린다 もし (r == 0) { r = b; } 결과 = 쓰는숫자.切り出す(r - 1, 1) + 결과; k = 数学.商(k - r, b); } } 返す 결과;}차례로(번호표){ k = 0; 繰り返す (i = 0; i < 번호표.長さ; i++) { 값 = 쓰는숫자.位置検索(번호표.切り出す(i, 1)); もし (!영있나) { 값 += 1; } k = k * b + 값; } 返す k;}q = コンソール.行読み().数に();繰り返す (i = 0; i < q; i++){ 칸 = コンソール.行読み().分ける(" "); もし (칸[0] == "I") { コンソール.書く(번호표로(칸[1].数に())); } でなければ { // 번호표는 50자리까지라 수로 바꾸지 않고 문자열 그대로 본다 コンソール.書く(차례로(칸[1]) + ""); }}