D - タイル張りの計画 / Tiling Plan 解説 by admin
Claude 4.6 Opus (Thinking)概要
\(N\) 個の長方形の部屋すべてを1種類の正方形タイル(一辺 \(d\))で敷き詰め、かつ各部屋のタイル枚数がデザイン係数 \(S_i\) の倍数になるような最大の \(d\) を求める問題です。
考察
条件の整理
一辺 \(d\) のタイルが条件を満たすには、各部屋 \(i\) について:
- 敷き詰め条件: \(d \mid H_i\) かつ \(d \mid W_i\)(タイルがぴったり収まる)
- デザイン条件: \(S_i \mid \frac{H_i}{d} \times \frac{W_i}{d}\)(枚数が \(S_i\) の倍数)
素因数ごとに独立に考える
\(d\) を最大化する問題を、各素因数 \(p\) ごとの指数 \(e = v_p(d)\)(\(d\) に含まれる \(p\) の指数)を最大化する問題に分解します。
各部屋 \(i\) について \(a = v_p(H_i)\), \(b = v_p(W_i)\), \(s = v_p(S_i)\) とおくと:
- 条件1: \(e \leq a\) かつ \(e \leq b\) → \(e \leq \min(a, b)\)
- 条件2: \((a - e) + (b - e) \geq s\) → \(e \leq \lfloor\frac{a + b - s}{2}\rfloor\)
したがって各素数 \(p\) に対する最大指数は: $\(e_{\max}(p) = \min_{i=1}^{N} \min\left(\min(a_i, b_i),\ \left\lfloor\frac{a_i + b_i - s_i}{2}\right\rfloor\right)\)$
どの素数を調べればよいか
条件1より \(e \leq \min(a_i, b_i)\) が全部屋で成り立つ必要があるので、\(e \leq v_p(G)\) です。ここで \(G = \gcd(\gcd(H_1, W_1), \gcd(H_2, W_2), \ldots)\) です。
\(G\) を割らない素数 \(p\) については \(v_p(G) = 0\) なので \(e_{\max}(p) = 0\)、つまり \(d\) に寄与しません。\(G\) の素因数だけを調べれば十分です。
アルゴリズム
- 全部屋の \(\gcd(H_i, W_i)\) の全体GCDとして \(G\) を計算する
- \(G\) を素因数分解する
- \(G\) の各素因数 \(p\) について、全部屋を走査して \(e_{\max}(p)\) を計算する
- 答えは \(\prod_p p^{e_{\max}(p)}\)
具体例
部屋が1つで \(H=12, W=18, S=6\) の場合: - \(\gcd(12, 18) = 6\)、\(G = 6 = 2^1 \times 3^1\) - \(p=2\): \(a=v_2(12)=2, b=v_2(18)=1, s=v_2(6)=1\) - 条件1: \(e \leq \min(2,1) = 1\) - 条件2: \(e \leq \lfloor(2+1-1)/2\rfloor = 1\) - → \(e_{\max}(2) = 1\) - \(p=3\): \(a=v_3(12)=1, b=v_3(18)=2, s=v_3(6)=1\) - 条件1: \(e \leq \min(1,2) = 1\) - 条件2: \(e \leq \lfloor(1+2-1)/2\rfloor = 1\) - → \(e_{\max}(3) = 1\) - 答え: \(2^1 \times 3^1 = 6\)
計算量
- 時間計算量: \(O(N \cdot (\sqrt{G_{\max}} / \ln G_{\max} + P))\)
- \(G\) の素因数分解に \(O(\sqrt{G})\)(\(G \leq 10^9\) なので高々 \(O(31623)\))
- \(G\) の異なる素因数の個数 \(P\) は高々約30個(\(2 \times 3 \times 5 \times \cdots < 10^9\))
- 各素数について全部屋の \(v_p\) を計算: 1部屋あたり \(O(\log_{p} V)\)
- 全体: おおよそ \(O(N \times 30 \times 30) = O(N \cdot 900)\) 程度
- 空間計算量: \(O(N)\)
実装のポイント
\(p\)-進付値 \(v_p(n)\) の計算は、\(n\) を \(p\) で割れる限り割り続ける単純なループで実装
\(e_{\max}\) が0以下になったら早期にbreakして無駄な計算を省く
\(G\) の素因数分解は \(G \leq 10^9\) なので試し割りで十分高速
制約で「\(d=1\) は常に条件を満たす」と保証されているため、答えが存在しない場合を心配する必要はない
ソースコード
import sys
import math
from functools import reduce
def factorize(n):
"""Return prime factorization as dict {prime: exponent}"""
factors = {}
d = 2
while d * d <= n:
while n % d == 0:
factors[d] = factors.get(d, 0) + 1
n //= d
d += 1
if n > 1:
factors[n] = factors.get(n, 0) + 1
return factors
def solve():
input_data = sys.stdin.buffer.read().split()
idx = 0
N = int(input_data[idx]); idx += 1
# For each room i, we need:
# 1) d | H_i and d | W_i
# 2) S_i | (H_i/d * W_i/d)
#
# Condition 1: d | gcd(H_i, W_i) for all i
# So d | G where G = gcd of all gcd(H_i, W_i)
#
# Condition 2: For each room i, S_i | (H_i * W_i / d^2)
# Let's think about this per prime p.
#
# For each prime p, let:
# a = v_p(H_i), b = v_p(W_i), s = v_p(S_i), e = v_p(d)
#
# Condition 1: e <= min(a, b) for all i => e <= v_p(G)
# Condition 2: (a - e) + (b - e) >= s => e <= (a + b - s) / 2 for all i
#
# We want to maximize d, so for each prime p we want to maximize e = v_p(d).
# e_max(p) = min over all i of: min(min(a_i, b_i), floor((a_i + b_i - s_i) / 2))
# But also e >= 0.
#
# We only need to consider primes that appear in some H_i, W_i, or S_i.
# Actually, we only need primes where the constraint might be binding.
# Since d | G, we only care about primes dividing G.
rooms = []
G = 0 # gcd of all gcd(H_i, W_i)
for i in range(N):
H = int(input_data[idx]); idx += 1
W = int(input_data[idx]); idx += 1
S = int(input_data[idx]); idx += 1
rooms.append((H, W, S))
g = math.gcd(H, W)
G = math.gcd(G, g) if i > 0 else g
# Factorize G
G_factors = factorize(G)
# For each prime p dividing G, compute the max exponent e
# e = min over all rooms of min(v_p(gcd(H_i,W_i)), floor((v_p(H_i)+v_p(W_i)-v_p(S_i))/2))
# Note: v_p(gcd(H_i,W_i)) = min(v_p(H_i), v_p(W_i))
def val_p(n, p):
"""p-adic valuation of n"""
if n == 0:
return float('inf')
v = 0
while n % p == 0:
n //= p
v += 1
return v
result = 1
for p, vG in G_factors.items():
# vG = min over all i of min(v_p(H_i), v_p(W_i))
# This is the upper bound from condition 1
e_max = vG # already the global min from condition 1
for H, W, S in rooms:
a = val_p(H, p)
b = val_p(W, p)
s = val_p(S, p)
# Condition 1 bound for this room: min(a, b) -- already captured in vG
# Condition 2 bound: floor((a + b - s) / 2)
bound2 = (a + b - s) // 2
e_max = min(e_max, bound2)
if e_max <= 0:
e_max = 0
break
e_max = max(e_max, 0)
result *= p ** e_max
print(result)
solve()
この解説は claude4.6opus-thinking によって生成されました。
投稿日時:
最終更新: