熱門文章

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]


2025年7月31日 星期四

ZeroJudge 解題筆記:b604. Center of Symmetry

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


ZeroJudge 題目連結:b604. Center of Symmetry

解題想法


比較直接的作法,先將所有的點座標 (x, y) 存入集合之中,同時計算所有點座標的平均值 (xc, yc)。接下來從集合中取出一個點,計算對稱點的座標 (xn, yn),檢查 (xn, yn) 是否在集合之中,如果沒有就可以印出 no 並停止檢查,反之從集合中移除 (xn, yn),重複這個過程直到集合清空為止。如果集合可以清空,印出 yes。
另一個作法,是將所有的點存入陣列、由小到大排序。因為排在陣列最前面的點,其對稱點一定在陣列的最後面。由陣列兩端向中間檢查,只要頭尾兩個點不對稱,就可以印出 no 並停止檢查。如果可以檢查完整個陣列,印出 yes。

Python 程式碼


寫法1,從集合 points 中取出點,找到對稱點並移除。使用時間約為 0.1 s,記憶體約為 5.8 MB,通過測試。
def solve(n):
    points = set()  # 儲存點 (x, y) 的集合
    xc, yc = 0, 0  # 中心點座標 (xc, yc)
    for _ in range(n):  # 讀取 n 個點
        x, y = map(int, input().split())  # 讀取點座標 (x, y)
        x *= 2; y *= 2  # 為了使 (xc, yc) 皆為整數,先將 (x, y) 乘以 2
        xc += x; yc += y  # 先計算加總
        points.add((x, y))  # 將 (x, y) 加入 points
    ### end of for loop ###
    xc //= n; yc //= n  # 除以 n,取平均
    while points:  # 如果 points 還有資料繼續執行
        x, y = points.pop()  # 從 points 隨機取出一筆資料
        xn, yn = 2*xc - x, 2*yc - y  # 計算對稱點座標 (xn, yn)
        if (xn, yn) not in points:  # 如果 (xn, yn) 不在 points 之中
            return False  # 回傳 False
        points.remove((xn, yn))  # 否則從 points 中移除 (xn, yn)
    ### end of while loop ###
    return True  # 跑到這行代表有解,回傳 True

while True:
    n = int(input())  # 讀取點數 n
    if n == 0: break  # 如果讀到 0 中止迴圈
    print("yes" if solve(n) else "no")

2025年7月30日 星期三

ZeroJudge 解題筆記:b603. 拋物線方程式

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


ZeroJudge 題目連結:b603. 拋物線方程式

解題想法


這題考數學。假設開口向上或向下的抛物線頂點為 $(h, k) = (x_1, y_1)$,其方程式為 $$ y = A(x-h)^2 + k = A(x-x_1)^2 + y_1 $$ 線上另一個點為 $(x_2, y_2)$,將這個點代入上式可得 $$ y_2 = A(x_2 - x_1)^2 + y_1 ~\Rightarrow~ A = \frac{y_2 - y_1}{(x_2 - x_1)^2} $$ 另外二次曲線的通式為 $$ y = Ax^2 + Bx + C $$ 將上式與第1式比較係數,由 $x$ 項可得 $$ B = -2Ax_1 = -2 \times \frac{y_2 - y_1}{(x_2 - x_1)^2} \times x_1 $$ 由常數項可得 $$ C = Ax_1^2 + y_1 = \frac{y_2 - y_1}{(x_2 - x_1)^2} \times x_1^2 + y_1 $$ 為了將係數化為整數,將每一項同乘以 $x_2 - x_1$,再換成此題要求的型式 $ay = bx^2 + cx + d$,4個係數分別為 $$ \begin{align*} a &= x_2 - x_1 \\ b &= (x_2 - x_1) A = \frac{y_2 - y_1}{x_2 - x_1} \\ c &= (x_2 - x_1) B = -\frac{2x_1 (y_2 - y_1)}{x_2 - x_1} \\ d &= (x_2 - x_1) C = \frac{x_1^2 (y_2 - y_1)}{x_2 - x_1} + y_1 (x_2 - x_1) \end{align*} $$ 因為 $a$ 必須是正值,最後要再檢查一下係數的正負號。由以上的公式得到的係數可能需要約分。

Python 程式碼


第9行的 math.gcd,我使用的 Python 版本為 3.10.12,可以一次輸入多個參數求最大公因數,但是 ZeroJudge 網站的 Python 版本為 3.6.9,每次只能輸入2個參數。使用時間約為 21 ms,記憶體約為 3.4 MB,通過測試。
from math import gcd

def solve(x1, y1, x2, y2):
    a = x2-x1
    b = (y2-y1)//(x2-x1)
    c = -2*x1*(y2-y1)//(x2-x1)
    d = x1*x1*(y2-y1)//(x2-x1) + y1*(x2-x1)
    if a < 0:
        a, b, c, d = -a, -b, -c, -d
    g = gcd(gcd(gcd(a, b), c), d)
    if g != 1:
        a, b, c, d = a//g, b//g, c//g, d//g
    return a, b, c, d

while True:
    try:
        x1, y1, x2, y2 = map(int, input().split())
        a, b, c, d = solve(x1, y1, x2, y2)
        print(f"{a:d}y = {b:d}x^2 + {c:d}x + {d:d}")
    except EOFError: break


2025年7月29日 星期二

ZeroJudge 解題筆記:b595. Special Touring Car Racing

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


ZeroJudge 題目連結:b595. Special Touring Car Racing

解題想法


依序掃過 i = 1 ~ n 站,先計算從起點直接開到第 i 站的成本,再用另一層 for 迴圈,計算以 j = 1 ~ i-1 站為前一個停靠站到第 i 站的成本,更新第 i 站最低成本及前一個停靠站。最後再從終點往起點找出各停靠站的編號,將編號反向輸出。

Python 程式碼


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

for line in sys.stdin:
    if not line.strip(): continue
    n = int(line)  # 讀取 n
    if n == 0: break  # 如果 n 等於 0 中止迴圈
    stop = [0] + list(map(int, sys.stdin.readline().split()))  # 讀取停靠站位置,加入 stop[0] = 0 
    cost = [float('inf')]*(n+1)  # 移動到第 i 站的成本,預設為極大的值
    prev = [0]*(n+1)  # 移動到第 i 站之前停靠的前一站
    for i in range(1, n+1):  # 依序檢查第 1 ~ n 站
        cost[i] = min(cost[i], (200 - stop[i])**2)  # 計算由開頭直接走到第 i 站的成本
        for j in range(1, i):  # 依序檢查由第 1 ~ i-1 站走到第 i 站的成本
            val = cost[j] + (200 - (stop[i] - stop[j]))**2
            if val < cost[i]:  # 如果 val 小於 cost[i] 目前的值
                prev[i] = j; cost[i] = val  # 更新 prev[i] 為 j、 cost[i] 為 val
    ### end of for loop ###
    ans = [n]  # 答案,先加入終點 n
    idx = n  # 由 prev 讀取資料的索引值
    while prev[idx] != 0:  # 如果 prev[idx] 不等於 0 繼續執行
        ans.append(prev[idx])  # 將 prev[idx] 加入 ans
        idx = prev[idx]  # 更新 idx 為 prev[idx]
    print(*([0] + ans[::-1]))  # 反序列印