2025年8月9日 星期六

ZeroJudge 解題筆記:c184. 盈虧互補

作者:王一哲
日期:2025年8月9日


ZeroJudge 題目連結:c184. 盈虧互補

解題想法


先找出 n 包含 1 的因數 fac 及加總 facSum,如果 facSum 等於 n,n 是完全數;如果 facSum 不等於 n,還要再找 facSum 的因數 fac2 及加總 facSum2,如果 facSum2 等於 n,則兩者是友好數。

Python 程式碼


用 set 儲存因數,用 join 組合要輸出的因數加總算式。使用時間約為 39 ms,記憶體約為 3.5 MB,通過測試。
n = int(input())  # 要測試數字 n
fac = {1}  # 儲存 n 的因數,先放入 1
for i in range(2, int(n**0.5)+1):
    if n%i == 0:
        fac.add(i)
        fac.add(n//i)
facSum = sum(fac)  # n 的因數加總
print("+".join(map(str, sorted(fac))) + "=" + f"{facSum:d}")  # 印出 n 的因數及加總
if n == facSum:  # 如果 n 等於 facSum
    print(f"{n:d} is perfect.")  # n 是完全數
elif len(fac) == 1:  # 如果 fac 只有 1
    print("=0")  # 印出 =0
    print(f"{n:d} has no friends.")  # 不可能有友好數
else:  # 其它狀況
    n2 = facSum  # 要檢測 facSum 是否為友好數
    fac2 = {1}  # 儲存 n2 的因數,先放入 1
    for i in range(2, int(n2**0.5)+1):
        if n2%i == 0:
            fac2.add(i)
            fac2.add(n2//i)
    facSum2 = sum(fac2)  # n2 的因數加總
    print("+".join(map(str, sorted(fac2))) + "=" + f"{facSum2:d}")  # 印出 n2 的因數及加總
    if facSum2 == n:  # 如果 facSum2 等於 n,兩者是友好數
        print(f"{n:d} and {n2:d} are friends.")
    else:  # 否則 n 沒有友好數
        print(f"{n:d} has no friends.")


2025年8月8日 星期五

ZeroJudge 解題筆記:b911. 我想跟Kevin借筷子系列4

作者:王一哲
日期:2025年8月8日


ZeroJudge 題目連結:b911. 我想跟Kevin借筷子系列4

解題想法


先列出幾種可能性
  1. n = 0, data = [0], ans = 0
  2. n = 1, data = [1], ans = 1
  3. n = 2, data = [1, 2] => [0, 1] => [0, 0], ans = 2
  4. n = 3, data = [1, 2, 3] => [1, 0, 1] => [0, 0, 0], ans = 2
  5. n = 4, data = [1, 2, 3, 4] => [1, 0, 1, 2] => [0, 0, 0, 1] => [0, 0, 0, 0], ans = 3
  6. n = 5, data = [1, 2, 3, 4, 5] => [1, 2, 0, 1, 2] => n = 2 的狀況,ans = 1 + 2 = 3
  7. n = 6, data = [1, 2, 3, 4, 5, 6] => [1, 2, 0, 1, 2, 3] => n = 3 的狀況,ans = 1 + 2 = 3
  8. n = 7, data = [1, 2, 3, 4, 5, 6, 7] => [1, 2, 3, 0, 1, 2, 3] => n = 3 的狀況,ans = 1 + 2 = 3
  9. n = 8, data = [1, 2, 3, 4, 5, 6, 7, 8] => [1, 2, 3, 0, 1, 2, 3, 4] => n = 4 的狀況,ans = 1 + 3 = 4
找出規律,數量 n 如果可以除以 2 則 ans 加 1,直到 n = 1 時 ans 加 1 或是 n = 2 時 ans 加 2。

Python 程式碼


使用時間約為 24 ms,記憶體約為 3.4 MB,通過測試。
import sys

for line in sys.stdin:
    n = int(line)
    ans = 0
    while n >= 1:
        if n == 1:
            ans += 1
            break
        elif n == 2:
            ans += 2
            break
        else:
            ans += 1
            n //= 2
    print(ans)

與上方的寫法邏輯相同,但改用遞迴,使用時間約為 24 ms,記憶體約為 3.4 MB,通過測試。
import sys

for line in sys.stdin:
    n = int(line)
    def solve(x, a=0):
        if x == 1: return 1
        if x == 2: return 2
        return 1 + solve(x//2, a)
    # end of def
    print(solve(n))


2025年8月7日 星期四

ZeroJudge 解題筆記:b897. 10219 - Find the ways !

作者:王一哲
日期:2025年8月7日


ZeroJudge 題目連結:b897. 10219 - Find the ways !

解題想法


這題就是在計算組合數 $$ C^n_k = \frac{n!}{(n-k)! k!} = \frac{n(n-1)(n-2) \dots (n-k+1)}{k(k-1)(k-2) \dots \times 2 \times 1} $$ 取 $\log_{10}$ $$ \begin{align*} ans &= \log_{10} n + \log_{10} (n-1) + \log_{10} (n-2) + \dots + \log_{10} (n-k+1) \\ & ~~~~- (\log_{10} k + \log_{10} (k-1) + \log_{10} (k-2) + \dots + 0) \end{align*} $$ 最後再無條件進位到整數即為答案。
改用史特靈公式取近似,由其中一個表達式史特靈級數 $$ \ln n! = n \ln n - n + \frac{1}{2} \ln (2 \pi n) + \frac{1}{12n} - \frac{1}{360n^3} + \dots $$ 若忽略高次項並用換底公式換成以10為底的對數 $$ \begin{align*} \frac{\log_{10} n!}{\log_{10} e} &= n \cdot \frac{\log_{10} n}{\log_{10} e} - n + \frac{1}{2} \cdot \frac{\log_{10} (2 \pi n)}{\log_{10} e} \\ \log_{10} n! &= n \log_{10} n - n \log_{10} e + \frac{1}{2} \log_{10} (2 \pi) + \frac{1}{2} \log_{10} n \\ \log_{10} n! &\approx (n + 0.5) \log_{10} n - 0.4343 n + 0.39909 \end{align*} $$ 因此 $C^n_k$ 取 $\log_{10}$ $$ \begin{align*} & \\ \log_{10} C^n_k \approx & (n + 0.5) \log_{10} n - 0.4343 n + 0.39909 \\ & - [(n-k + 0.5) \log_{10} (n-k) - 0.4343 (n-k) + 0.39909] \\ & - [(k + 0.5) \log_{10} k - 0.4343 k + 0.39909] \end{align*} $$ 但是上式的第二行中有 $\log_{10}(n-k)$,當 $n=k$ 時會出問題,程式碼之中需要排除這個特例。

Python 程式碼


理論上用 math 函式庫中的 comb 可以很方便地計算組合數,但是這個函式在 Python 3.8 才加入,ZeroJudge 網站的 Python 版本為 3.6.9,不能用。
from math import comb, log10, ceil
import sys

for line in sys.stdin:
    n, k = map(int, line.split())
    print(ceil(log10(comb(n, k))))

改成不使用 comb,直接取 log10 ,還是會超時。
from math import log10, ceil
import sys

for line in sys.stdin:
    n, k = map(int, line.split())
    if n == k: print("1")
    else:
        ans = 0
        for i in range(n, n-k, -1): ans += log10(i)
        for i in range(k, 1, -1): ans -= log10(i)
        print(ceil(ans))

使用時間約為 17 ms,記憶體約為 3.3 MB,通過測試。
from math import log10, ceil
import sys

def fac(x):
    return (x+0.5)*log10(x) - 0.4343*x + 0.39909

for line in sys.stdin:
    n, k = map(int, line.split())
    if n == k: print("1")
    else: print(ceil(fac(n) - fac(n-k) - fac(k)))


2025年8月6日 星期三

ZeroJudge 解題筆記:b893. 勘根定理

作者:王一哲
日期:2025年8月6日


ZeroJudge 題目連結:b893. 勘根定理

解題想法


因為 $-2147483647 \leq x^6 \leq 2147483647 ~\Rightarrow -36 \leq x \leq 36$,只要一路由 $-36$ 每次加 1 檢測到 $36$ 即可。 這題的敘述有問題,題目中說無解時輸出 N0THING! >\\<,但實際上是 N0THING! >\\\<。

Python 程式碼


使用時間約為 19 ms,記憶體約為 3.3 MB,通過測試。
def func(x):
    return a*x**5 + b*x**4 + c*x**3 + d*x**2 + e*x + f

a, b, c, d, e, f = map(int, input().split())
if a == b == c == d == e == f == 0:
    print("Too many... = =\"")
else:
    real = False
    for i in range(-36, 36):
        if func(i) == 0:
            print(f"{i:d} {i:d}")
            real = True
        elif func(i)*func(i+1) < 0:
            print(f"{i:d} {i+1:d}")
            real = True
    if not real: print("N0THING! >\\\\\\<")


2025年8月5日 星期二

ZeroJudge 解題筆記:b877. 我是電視迷

作者:王一哲
日期:2025年8月5日


ZeroJudge 題目連結:b877. 我是電視迷

解題想法


這題可以用 if 判斷頻道號碼是否已經超過上限,從最前面繞回來;也可以取餘數找下一個頻道號碼。

Python 程式碼


使用時間約為 18 ms,記憶體約為 3.3 MB,通過測試。
a, b = map(int, input().split())
if b > a: print(b-a)
else: print(100-a+b)

將第2、3行合併。使用時間約為 17 ms,記憶體約為 3.3 MB,通過測試。
a, b = map(int, input().split())
print(b-a if b > a else 100-a+b)

取餘數,因為 Python 的取餘數若遇到被除數是負值,會先將被除數加上除數之後再取餘數,餘數和除數同號,不需要像 C++ 要在程式碼中自己加上除數量值。使用時間約為 18 ms,記憶體約為 3.3 MB,通過測試。
a, b = map(int, input().split())
print((b-a)%100)


2025年8月4日 星期一

ZeroJudge 解題筆記:b860. 獨角蟲進化計算器

作者:王一哲
日期:2025年8月4日


ZeroJudge 題目連結:b860. 獨角蟲進化計算器

解題想法


用 while 迴圈不斷更新糖果、獨角蟲、鐵殼蛹的數量,直到獨角蟲的數量歸零為止。

Python 程式碼


使用時間約為 46 ms,記憶體約為 3.3 MB,通過測試。
c, w = map(int, input().split())  # 讀取糖果數量 c,獨角蟲數量 w
k = 0  # 鐵殼蛹數量 k

while w > 0:  # 如果 w 大於 0 繼續執行
    if c >= 12 and w >= 1:  # 如果 c 大於等於 12 而且 w 大於等於 1 才能進化
        c -= 10  # c 減 10,先用掉 12 顆,進化得 1 顆,再將鐵殼蛹送給博士得 1 顆
        w -= 1  # 進化,獨角蟲數量 w 減 1
        k += 1  # 鐵殼蛹數量 k 加 1
    else:  # 否則將獨角蟲送給博士換糖果
        w -= 1
        c += 1
# end of while loop
print(k)  # 印出答案


2025年8月3日 星期日

ZeroJudge 解題筆記:b836. kevin戀愛攻略系列題-2 說好的霸王花呢??

作者:王一哲
日期:2025年8月3日


ZeroJudge 題目連結:b836. kevin戀愛攻略系列題-2 說好的霸王花呢??

解題想法


共有 $n$ 片花瓣,第一次拔1片,接下來每次比上一次多拔 $m$ 片,假設共拔 $k$ 次,拔掉的花瓣總數為 $$ t = k + [m + 2m + 3m + \dots + (k-1)m] = k + \frac{(k-1)km}{2} = \frac{1}{2} [m k^2 + (2-m) k] $$ 若要剛好拔完,則 $t = n$,且 $k \in N$ 才能符合條件,因此 $$ mk^2 + (2-m)k - 2n = 0 $$ 若有正整數的根,輸出 "Go Kevin!!",否則輸出 "No Stop!!"。

Python 程式碼


使用時間約為 18 ms,記憶體約為 3.3 MB,通過測試。
import sys, math

for line in sys.stdin:  # 讀取多行測資直到 EOF 為止
    n, m = map(int, line.split())  # 花瓣數量 n,每次多拔的花瓣數量 m
    if m == 0:  # 如果 m 等於 0 一定有解
        print("Go Kevin!!")  # 印出 Go Kevin!!,繼續檢測下一行
        continue
    a, b, c = m, 2-m, -2*n  # 一元二次方程式的係數
    D = b*b - 4*a*c  # 判別式
    if D < 0:  # 判別式小於 0 無實數解
        print("No Stop!!")  # 印出 No Stop!!,繼續檢測下一行
        continue
    x1 = (-b + math.sqrt(D)) / (2*a)  # 較大的根
    x2 = (-b - math.sqrt(D)) / (2*a)  # 較小的根
    if x1 > 0 and int(x1) == math.ceil(x1):  # 如果 x1 是正整數
        print("Go Kevin!!")  # 印出 Go Kevin!!,繼續檢測下一行
        continue
    elif x2 > 0 and int(x2) == math.ceil(x2):  # 如果 x2 是正整數
        print("Go Kevin!!")  # 印出 Go Kevin!!,繼續檢測下一行
        continue
    else: print("No Stop!!")  # 否則印出 No Stop!!


2025年8月2日 星期六

ZeroJudge 解題筆記:b762. 英國聯蒙

作者:王一哲
日期:2025年8月2日


ZeroJudge 題目連結:b762. 英國聯蒙

解題想法


這題用字典或串列儲存對應的訊息,寫起來會比較簡潔。

Python 程式碼


使用時間約為 18 ms,記憶體約為 3.4 MB,通過測試。
act = {"Get_Kill": 1, "Get_Assist": 2, "Die": 3}  # 輸入的狀態
# 連續擊殺數對應的訊息
kill_mess = ["",
             "You have slain an enemie.",
             "You have slain an enemie.",
             "KILLING SPREE!",
             "RAMPAGE~",
             "UNSTOPPABLE!",
             "DOMINATING!",
             "GUALIKE!",
             "LEGENDARY!"]
# 陣亡時連續擊殺數對應的訊息
die_mess = [ "You have been slained.",
             "SHUTDOWN."]

N = int(input())  # 指令數量
cont = 0  # 連續擊殺數
kill = 0  # 總擊殺數
ast = 0  # 助攻次數
die = 0  # 陣亡次數
for _ in range(N):
    a = act[input().strip()]  # 讀取 N 行指令,要用 strip() 刪除某些測資後方多的 \t 或空白
    if a == 1:  # 如果是 Get_Kill
        cont += 1  # 連續擊殺數加 1
        if cont >= 8: print(kill_mess[8])  # 如果連續擊殺數大於等於 8,印出 kill_mess[8]
        else: print(kill_mess[cont])  # 否則印出 kill_mess[cont]
    elif a == 2: ast += 1  # 如果是 Get_Assist 助攻數加 1
    elif a == 3:  # 如果是 Die
        if cont < 3: print(die_mess[0])  # 如果連續擊殺數小於 3,印出 die_mess[0]
        else: print(die_mess[1])  # 否則印出 die_mess[1]
        die += 1  # 陣亡次數加 1
        kill += cont  # 更新擊殺總數
        cont = 0  # 連續擊殺數歸零
kill += cont  # 最後要再加上一次連續擊殺數
print(f"{kill:d}/{die:d}/{ast:d}")  # 印出答案


2025年8月1日 星期五

ZeroJudge 解題筆記:b759. 我明明就有說過= =

作者:王一哲
日期:2025年8月1日


ZeroJudge 題目連結:b759. 我明明就有說過= =

解題想法


這題可以只用字串切片或是索引值組合出每次要印出的內容,也可以每次都更新字串的內容,兩種方法的速度都很快。

Python 程式碼


使用時間約為 21 ms,記憶體約為 3.3 MB,通過測試。
X = input()
for i in range(len(X)):
    print(X[i:]+X[:i])

每次印出字串後,重新組合字串。使用時間約為 20 ms,記憶體約為 3.3 MB,通過測試。
X = input()
for i in range(len(X)):
    print(X)
    X = X[1:] + X[0]