把一个用 a 进制写的数转换成 b 进制输出。 这个数不小于 0,小数点后可能无限循环。 循环的部分只写一次并用括号括起来:十进制的 0.1666… 写作 0.1(6),0.142857142857… 写作 0.(142857)。 数位的值为 10 及以上时用英文大写字母:A = 10,B = 11,…,Z = 35。 输入的数是 I、I.F、I.F(R) 三种形式之一。 I 是整数部分,至少一位,除非就是一个字符 0,否则不以 0 开头。 F 是紧跟在小数点后的不循环部分,可以为空;R 是循环部分,至少一位。 有小数点时,F 和 R 不会同时为空。 输入的小数部分不一定是最简写法:也会出现 0.50、0.1(0)、0.12(32)、0.(9) 这样的写法。 输出是同一个值用 b 进制写出的唯一写法。 整数部分不带多余的前导 0(整数部分为 0 时就是一个字符 0)。 值是整数时不写小数点。 小数点后是有限位时不加括号,且最后一位不是 0。 小数点后无限延续时,让循环部分尽可能早地开始,并在其中选最短的一段用括号括起来。 不使用以 (b−1) 无限循环结尾的写法:十进制的 0.(9) 与 1 相等,所以写作 1。 第一行给出用一个空格隔开的 a 和 b,第二行给出用 a 进制写的数,其中没有空格。
输入
2 ≤ a, b ≤ 36。 整数部分 I 的值按十进制小于 10⁹。 设小数点后写出的位数(包括括号内)为 d,则 a^d ≤ 10,000。 每一位是 0~9 或 A~Z,且值小于 a。
输出
一行:用 b 进制写的数(大写,符合上述规则的唯一写法)
样例 1 · stdin
10 2
0.1
stdout
0.0(0011)
样例 2 · stdin
10 6
12.4(9)
stdout
20.3
main.py
错误列表00F8 ↓ · ⇧F8 ↑
输入(stdin)
运行时这些行会依次作为 input()/标准输入传入(运行中不会等待输入)。
$ 就绪
请在电脑上打开以编写和运行代码。在手机上可以查看题目和题解。
其他语言怎么写?查看各语言题解 · 8 种语言题解
先化成分数,再做竖式除法。 a 进制的 I.F(R) 中 F 有 k 位、R 有 m 位时,值为 p / q,q = a^k × (a^m − 1),p = I × q + F × (a^m − 1) + R(没有 R 时 q = a^k,p = I × q + F)。 把 p 除以 q 的商写成 b 进制;对余数 r,r × b 除以 q 的商就是下一位,余数成为新的 r。 r 变成 0 就结束;之前见过的 r 再次出现时,就从那个 r 产生的那一位开始循环。 r 小于 q,所以 q 步之内必有其一发生。 这样除下去,自然得到最早开始、最短的循环节,也不会出现 (b−1) 无限循环的写法。 每个 r 在何时出现过,Python 用字典记录,其他语言用 q 个格子的数组(q ≤ 10,000;Craykon 用 新列表(q, -1))。 读数位的方式各语言不同:Python int(t, a)、Java Long.parseLong(t, a)、JavaScript/TypeScript parseInt(t, a) 能直接读 2~36 进制,但传入空的 F 时 Python、Java 会抛异常,JavaScript 得到 NaN,所以空字符串要单独当作 0 处理。 C# 的 Convert.ToInt64(t, base) 只接受 2、8、10、16 进制,所以用 Digits.IndexOf(ch);C 用 strchr(DIGITS, c) - DIGITS,C++ 用 DIGITS.find(ch),Craykon 用 DIGITS.查找位置(ch) 求数位的值。 陷阱一:把整数部分和小数部分分开转换,遇到像 0.(9) 这样小数部分恰好为 1 的数就会出错。 9 / 9 的第一位是 10,十进制下输出 0.A。 要把整个 p 除以 q,1 才会进到整数部分。 陷阱二:p 会接近 10⁹ × 10⁴ = 10¹³,在 Java、C#、C、C++ 的 int 中会溢出——要用 long/long long(Python 的整数没有上限,JavaScript 和 Craykon 在 2^53 以内是精确的,所以没问题)。 陷阱三:r 的位置要在用它产生数位之前记下。 放入数位之后再记,十进制的 0.1(6) 会变成 0.16()。 陷阱四:r 变成 0 就要立刻停止。 再除一次 0 会在末尾多出一个 0,十进制的 0.5 在二进制下变成 0.10。 陷阱五:整数部分为 0 时 while (v > 0) 一次也不执行,0.1 会变成 .1(C、C++、C# 的解答用 do-while 避免了)。 陷阱六:JavaScript/TypeScript 的 v.toString(b) 和 Java 的 Long.toString(v, b) 输出小写,需要 toUpperCase()。 陷阱七:在 C、C++、Java、C# 中用 c - '0' 求数位的值,'A' 会变成 17 而不是 10。
Pythonmain.py · 49 行
DIGITS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"a, b = map(int, input().split())s = input().strip()# 정수 부분 / 끝나는 부분 F / 되풀이 부분 R 로 나눈다int_str, _, frac = s.partition(".")fixed, _, rep = frac.partition("(")rep = rep.rstrip(")")# 값을 분수 p / q 로 만든다: q = a^k × (a^m − 1) (R가 없으면 a^k)k, m = len(fixed), len(rep)f_val = int(fixed, a) if fixed else 0if m == 0: q = a ** k num = f_valelse: r_val = int(rep, a) q = a ** k * (a ** m - 1) num = f_val * (a ** m - 1) + r_valp = int(int_str, a) * q + num # 소수 부분이 1이 될 수도 있으니(0.(9)) 한꺼번에 나눈다whole, r = divmod(p, q)# 정수 부분을 b진법으로head = []while True: head.append(DIGITS[whole % b]) whole //= b if whole == 0: breakout = "".join(reversed(head))# 나머지를 b배 하며 긴 나눗셈. 같은 나머지가 다시 나오면 거기서부터 되풀이된다seen = {} # 나머지 → 그 나머지로 만든 자리의 위치digits = []while r != 0 and r not in seen: seen[r] = len(digits) r *= b digits.append(DIGITS[r // q]) r %= qif digits: if r == 0: out += "." + "".join(digits) else: start = seen[r] out += "." + "".join(digits[:start]) + "(" + "".join(digits[start:]) + ")"print(out)
JavaScriptmain.js · 43 行
const DIGITS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";const [line1, line2] = require("fs").readFileSync(0, "utf8").trim().split(/\r?\n/);const [a, b] = line1.split(" ").map(Number);const s = line2.trim();// a진법 글자들을 값으로(빈 문자열이면 0). 값은 10^13쯤이라 2^53 안이다const read = (t) => (t === "" ? 0 : parseInt(t, a));// 정수 부분 / 끝나는 부분 / 되풀이 부분으로 나눈다const m = s.match(/^([0-9A-Z]+)(?:\.([0-9A-Z]*)(?:\(([0-9A-Z]+)\))?)?$/);const intStr = m[1], fixed = m[2] ?? "", rep = m[3] ?? "";// 값을 분수 p / q 로: q = a^k × (a^m − 1), 되풀이 부분이 없으면 a^klet q = a ** fixed.length;let num = read(fixed);if (rep !== "") { const am1 = a ** rep.length - 1; q *= am1; num = num * am1 + read(rep);}const p = read(intStr) * q + num; // 소수 부분이 1일 수도 있으니(0.(9)) 한꺼번에 나눈다let out = Math.floor(p / q).toString(b).toUpperCase(); // toString은 소문자를 낸다let r = p % q;// 긴 나눗셈: 같은 나머지가 다시 나오면 거기서부터 되풀이된다const seen = new Array(q).fill(-1); // 나머지 → 그 나머지로 만든 자리의 위치const digits = [];while (r !== 0 && seen[r] === -1) { seen[r] = digits.length; r *= b; digits.push(DIGITS[Math.floor(r / q)]); r %= q;}if (digits.length > 0) { if (r === 0) out += "." + digits.join(""); else { const start = seen[r]; out += "." + digits.slice(0, start).join("") + "(" + digits.slice(start).join("") + ")"; }}console.log(out);
TypeScriptmain.ts · 46 行
import { readFileSync } from "node:fs";const DIGITS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";const [line1, line2] = readFileSync(0, "utf8").trim().split(/\r?\n/);const [a, b]: number[] = line1.split(" ").map(Number);const s: string = line2.trim();// a진법 글자들을 값으로(빈 문자열이면 0). 값은 10^13쯤이라 2^53 안이다const read = (t: string): number => (t === "" ? 0 : parseInt(t, a));// 정수 부분 / 끝나는 부분 / 되풀이 부분으로 나눈다const m = s.match(/^([0-9A-Z]+)(?:\.([0-9A-Z]*)(?:\(([0-9A-Z]+)\))?)?$/)!;const intStr: string = m[1];const fixed: string = m[2] ?? "";const rep: string = m[3] ?? "";// 값을 분수 p / q 로: q = a^k × (a^m − 1), 되풀이 부분이 없으면 a^klet q: number = a ** fixed.length;let num: number = read(fixed);if (rep !== "") { const am1 = a ** rep.length - 1; q *= am1; num = num * am1 + read(rep);}const p: number = read(intStr) * q + num; // 소수 부분이 1일 수도 있으니(0.(9)) 한꺼번에 나눈다let out: string = Math.floor(p / q).toString(b).toUpperCase(); // toString은 소문자를 낸다let r: number = p % q;// 긴 나눗셈: 같은 나머지가 다시 나오면 거기서부터 되풀이된다const seen: number[] = new Array<number>(q).fill(-1); // 나머지 → 그 나머지로 만든 자리의 위치const digits: string[] = [];while (r !== 0 && seen[r] === -1) { seen[r] = digits.length; r *= b; digits.push(DIGITS[Math.floor(r / q)]); r %= q;}if (digits.length > 0) { if (r === 0) out += "." + digits.join(""); else { const start = seen[r]; out += "." + digits.slice(0, start).join("") + "(" + digits.slice(start).join("") + ")"; }}console.log(out);
JavaMain.java · 71 行
import java.io.BufferedReader;import java.io.InputStreamReader;import java.util.Arrays;public class Main { static final String DIGITS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; // a진법 글자들을 값으로(빈 문자열이면 0) static long read(String t, int a) { return t.isEmpty() ? 0 : Long.parseLong(t, a); } public static void main(String[] args) throws Exception { BufferedReader in = new BufferedReader(new InputStreamReader(System.in)); String[] ab = in.readLine().trim().split(" "); int a = Integer.parseInt(ab[0]), b = Integer.parseInt(ab[1]); String s = in.readLine().trim(); // 정수 부분 / 끝나는 부분 / 되풀이 부분으로 나눈다 String intStr = s, fixed = "", rep = ""; int dot = s.indexOf('.'); if (dot >= 0) { intStr = s.substring(0, dot); String frac = s.substring(dot + 1); int open = frac.indexOf('('); if (open >= 0) { fixed = frac.substring(0, open); rep = frac.substring(open + 1, frac.length() - 1); } else { fixed = frac; } } // 값을 분수 p / q 로: q = a^k × (a^m − 1), 되풀이 부분이 없으면 a^k long q = 1; for (int i = 0; i < fixed.length(); i++) q *= a; long num = read(fixed, a); if (!rep.isEmpty()) { long am1 = 1; for (int i = 0; i < rep.length(); i++) am1 *= a; am1 -= 1; q *= am1; num = num * am1 + read(rep, a); } long p = read(intStr, a) * q + num; // 10^13까지 커지니 long. 소수 부분이 1일 수도 있어 한꺼번에 나눈다 StringBuilder out = new StringBuilder(Long.toString(p / q, b).toUpperCase()); // toString은 소문자를 낸다 int r = (int) (p % q); // q < 10000이라 나머지는 int에 들어간다 // 긴 나눗셈: 같은 나머지가 다시 나오면 거기서부터 되풀이된다 int[] seen = new int[(int) q]; // 나머지 → 그 나머지로 만든 자리의 위치 Arrays.fill(seen, -1); StringBuilder digits = new StringBuilder(); while (r != 0 && seen[r] == -1) { seen[r] = digits.length(); r *= b; digits.append(DIGITS.charAt((int) (r / q))); r %= q; } if (digits.length() > 0) { out.append('.'); if (r == 0) out.append(digits); else { int start = seen[r]; out.append(digits, 0, start).append('(').append(digits, start, digits.length()).append(')'); } } System.out.println(out); }}
C#Main.cs · 86 行
using System;using System.Text;class Program{ const string Digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; // a진법 글자들을 값으로(빈 문자열이면 0). Convert.ToInt64(s, base)는 2·8·10·16진법만 받는다 static long Read(string t, int a) { long v = 0; foreach (char ch in t) v = v * a + Digits.IndexOf(ch); return v; } static void Main() { string[] ab = Console.ReadLine().Split(' ', StringSplitOptions.RemoveEmptyEntries); int a = int.Parse(ab[0]), b = int.Parse(ab[1]); string s = Console.ReadLine().Trim(); // 정수 부분 / 끝나는 부분 / 되풀이 부분으로 나눈다 string intStr = s, fixedPart = "", rep = ""; int dot = s.IndexOf('.'); if (dot >= 0) { intStr = s[..dot]; string frac = s[(dot + 1)..]; int open = frac.IndexOf('('); if (open >= 0) { fixedPart = frac[..open]; rep = frac[(open + 1)..^1]; } else fixedPart = frac; } // 값을 분수 p / q 로: q = a^k × (a^m − 1), 되풀이 부분이 없으면 a^k long q = 1; for (int i = 0; i < fixedPart.Length; i++) q *= a; long num = Read(fixedPart, a); if (rep.Length > 0) { long am1 = 1; for (int i = 0; i < rep.Length; i++) am1 *= a; am1 -= 1; q *= am1; num = num * am1 + Read(rep, a); } long p = Read(intStr, a) * q + num; // 10^13까지 커지니 long. 소수 부분이 1일 수도 있어 한꺼번에 나눈다 long whole = p / q; var head = new StringBuilder(); do { head.Insert(0, Digits[(int)(whole % b)]); // 낮은 자리부터 나오니 앞에 끼운다 whole /= b; } while (whole > 0); long r = p % q; // 긴 나눗셈: 같은 나머지가 다시 나오면 거기서부터 되풀이된다 int[] seen = new int[q]; // 나머지 → 그 나머지로 만든 자리의 위치 Array.Fill(seen, -1); var digits = new StringBuilder(); while (r != 0 && seen[r] == -1) { seen[r] = digits.Length; r *= b; digits.Append(Digits[(int)(r / q)]); r %= q; } var outSb = new StringBuilder(head.ToString()); if (digits.Length > 0) { string d = digits.ToString(); if (r == 0) outSb.Append('.').Append(d); else { int start = seen[r]; outSb.Append('.').Append(d[..start]).Append('(').Append(d[start..]).Append(')'); } } Console.WriteLine(outSb); }}
C++main.cpp · 79 行
#include <iostream>#include <string>#include <vector>#include <algorithm>const std::string DIGITS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";// a진법 글자들을 값으로(빈 문자열이면 0)long long readPart(const std::string &t, int a) { long long v = 0; for (char ch : t) v = v * a + (long long)DIGITS.find(ch); return v;}long long power(int a, std::size_t k) { long long v = 1; for (std::size_t i = 0; i < k; i++) v *= a; return v;}int main() { int a, b; std::string s; std::cin >> a >> b >> s; // 정수 부분 / 끝나는 부분 / 되풀이 부분으로 나눈다 std::string intStr = s, fixed, rep; auto dot = s.find('.'); if (dot != std::string::npos) { intStr = s.substr(0, dot); std::string frac = s.substr(dot + 1); auto open = frac.find('('); if (open != std::string::npos) { fixed = frac.substr(0, open); rep = frac.substr(open + 1, frac.size() - open - 2); } else { fixed = frac; } } // 값을 분수 p / q 로: q = a^k × (a^m − 1), 되풀이 부분이 없으면 a^k long long q = power(a, fixed.size()); long long num = readPart(fixed, a); if (!rep.empty()) { long long am1 = power(a, rep.size()) - 1; q *= am1; num = num * am1 + readPart(rep, a); } long long p = readPart(intStr, a) * q + num; // 10^13까지 커지니 long long long long whole = p / q; // 소수 부분이 1일 수도 있으니(0.(9)) 한꺼번에 나눈다 std::string out; do { out += DIGITS[whole % b]; whole /= b; } while (whole > 0); std::reverse(out.begin(), out.end()); // 긴 나눗셈: 같은 나머지가 다시 나오면 거기서부터 되풀이된다 std::vector<int> seen(q, -1); // 나머지 → 그 나머지로 만든 자리의 위치 std::string digits; long long r = p % q; while (r != 0 && seen[r] == -1) { seen[r] = (int)digits.size(); r *= b; digits += DIGITS[r / q]; r %= q; } if (!digits.empty()) { out += '.'; if (r == 0) out += digits; else { int start = seen[r]; out += digits.substr(0, start) + "(" + digits.substr(start) + ")"; } } std::cout << out << '\n';}
Cmain.c · 76 行
#include <stdio.h>#include <string.h>static const char *DIGITS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";/* s[from..to) 의 a진법 글자들을 값으로(비어 있으면 0) */static long long read_part(const char *s, int from, int to, int a) { long long v = 0; for (int i = from; i < to; i++) v = v * a + (long long)(strchr(DIGITS, s[i]) - DIGITS); /* 'A'는 10: c - '0'이 아니다 */ return v;}static long long power(int a, int k) { long long v = 1; while (k-- > 0) v *= a; return v;}int main(void) { int a, b; char s[128]; if (scanf("%d %d %127s", &a, &b, s) != 3) return 0; int len = (int)strlen(s); /* 정수 부분 [0, dot) / 끝나는 부분 [dot+1, open) / 되풀이 부분 [open+1, len-1) */ char *pdot = strchr(s, '.'); int dot = pdot ? (int)(pdot - s) : len; char *popen = strchr(s, '('); int open = popen ? (int)(popen - s) : len; int fixed_from = dot < len ? dot + 1 : len; /* 값을 분수 p / q 로: q = a^k × (a^m − 1), 되풀이 부분이 없으면 a^k */ long long q = power(a, open - fixed_from); long long num = read_part(s, fixed_from, open, a); if (open < len) { long long am1 = power(a, len - 1 - (open + 1)) - 1; q *= am1; num = num * am1 + read_part(s, open + 1, len - 1, a); } long long p = read_part(s, 0, dot, a) * q + num; /* 10^13까지 커지니 long long */ long long whole = p / q; /* 소수 부분이 1일 수도 있으니(0.(9)) 한꺼번에 나눈다 */ char head[64]; int hl = 0; do { head[hl++] = DIGITS[whole % b]; whole /= b; } while (whole > 0); for (int i = hl - 1; i >= 0; i--) putchar(head[i]); /* 긴 나눗셈: 같은 나머지가 다시 나오면 거기서부터 되풀이된다 */ static int seen[10000]; /* 나머지 → 그 나머지로 만든 자리의 위치 */ static char digits[10001]; for (int i = 0; i < q; i++) seen[i] = -1; int n = 0; long long r = p % q; while (r != 0 && seen[r] == -1) { seen[r] = n; r *= b; digits[n++] = DIGITS[r / q]; r %= q; } digits[n] = '\0'; if (n > 0) { putchar('.'); if (r == 0) fputs(digits, stdout); else { int start = seen[r]; printf("%.*s(%s)", start, digits, digits + start); } } putchar('\n'); return 0;}
Craykonmain.cray · 99 行
DIGITS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";// a진법 글자들을 앞에서부터 읽어 값으로 만든다(빈 문자열이면 0)읽기(s, a){ v = 0; 遍历 (ch 在 s) { v = v * a + DIGITS.查找位置(ch); } 返回 v;}거듭(a, k){ v = 1; 循环 (i = 0; i < k; i += 1) { v = v * a; } 返回 v;}ab = 控制台.读数字();a = ab[0];b = ab[1];s = 控制台.读行().去空白();// 정수 부분 / 끝나는 부분 / 되풀이 부분으로 나눈다intStr = s;fixed = "";rep = "";dot = s.查找位置(".");如果 (dot >= 0){ intStr = s.截取(0, dot); frac = s.截取(dot + 1); open = frac.查找位置("("); 如果 (open >= 0) { fixed = frac.截取(0, open); rep = frac.截取(open + 1, frac.长度 - open - 2); } 否则 { fixed = frac; }}// 값을 분수 p / q 로: q = a^k × (a^m − 1), 되풀이 부분이 없으면 a^kq = 거듭(a, fixed.长度);num = 읽기(fixed, a);如果 (rep.长度 > 0){ am1 = 거듭(a, rep.长度) - 1; q = q * am1; num = num * am1 + 읽기(rep, a);}p = 읽기(intStr, a) * q + num; // 소수 부분이 1일 수도 있으니(0.(9)) 한꺼번에 나눈다whole = 数学.商(p, q);r = p % q;// 정수 부분을 b진법으로out = "";当 (真){ out = DIGITS[whole % b] + out; whole = 数学.商(whole, b); 如果 (whole == 0) { 跳出; }}// 긴 나눗셈: 같은 나머지가 다시 나오면 거기서부터 되풀이된다seen = 新列表(q, -1); // 나머지 → 그 나머지로 만든 자리의 위치digits = [];当 (r != 0 && seen[r] == -1){ seen[r] = digits.数量; r = r * b; digits.添加(DIGITS[数学.商(r, q)]); r = r % q;}如果 (digits.数量 > 0){ 如果 (r == 0) { out = out + "." + digits.连接(""); } 否则 { start = seen[r]; out = out + "." + digits.子列表(0, start).连接("") + "(" + digits.子列表(start, digits.数量 - start).连接("") + ")"; }}控制台.输出(out);