CADDi 2018
C - Product and GCD
問題
- $N$ 個の 正整数 $a_1,a_2,...,a_N$ がある
- 各値は不明だが、$a_1 \times a_2 \times ... \times a_N=P$ がわかっている
- $a_1,a_2,...,a_N$ の最大公約数として考えられるものの最大値を求めよ
例
$N = 3, P = 120$ の場合、2, 6, 10 などで「2」が最大公約数となる
解法
ある数 $k$ が $N$ 個の数の最大公約数なら、$P$ は $k$ が $N$ 回掛け合わされているので、$k^N$ の倍数でないといけない。
逆に考えると、$P$ を素因数分解したときに $N$ 以上の指数がある素因数は、各 $a_i$ に均等に分配した方がよい。
$P=p_1^a p_2^b p_3^c ...$ とすると、答えは $p_1^{\frac{a}{N}} p_2^{\frac{b}{N}} p_3^{\frac{c}{N}} ...$ となる(剰余切り捨て)
from collections import defaultdict
def solve(n, p):
if n == 1:
return p
i = 2
ps = defaultdict(lambda: 0)
while i * i <= p:
while p % i == 0:
p //= i
ps[i] += 1
i += 1
ans = 1
for p, c in ps.items():
ans *= p ** (c // n)
return ans
n, p = map(int, input().split())
print(solve(n, p))
D - Harlequin
問題
- $N$ 色のりんごがあり、$N$ 種類の色には1から $N$ までの番号が振られている
- $i$ 番の色のりんごは $a_i$ 個ある
- 以下のゲームを、交互に行う
- 木から1個以上のりんごを選んで食べる
- 一度に選ぶりんごは全て異なる色でなければならない
- 木から最後のりんごを食べた者を勝者とする
- 勝つのは先手、後手どちらか
- $1 \le N \le 10^5$
- $1 \le a_i \le 10^9$
解法
「横向きNim」として解いたが、何か間違ってる予感……。
石取りゲーム(Nim) は、複数の石山があり、互いに石を1個以上ずつ取っていって、最後に取った方が勝ちというゲーム。一度に1つの石山からしか取ることができない。
結論だけ言うと、初期状態で、各山の石の個数のXORが0なら後手必勝、それ以外なら先手必勝となる。
それを横向きにすると、今回の問題とよく似ているように見える。
Nim この問題 ○ 色1 ○ ○ ○ 色2 ○ ○ ○ ○ 色3 ○ ○ ○ ○ ○ 色4 ○ ○ ○ 山1 山2 山3 どちらも、一度に縦方向にしか取ることはできない
なので、1~$max(a_i)$ のそれぞれで、「$k$ 個以上ある色はいくつあるか」を求めれば、それを各山の石の個数と見做すことで、Nimにできる。
ただ、$a_i \le 10^9$ なので愚直にやるとTLE。
ここで、石の個数が同じ2つの山=XORで互いに打ち消し合うことを考えれば、リンゴの個数が少ない順にソートして、1つ前の個数との差分が偶数なら全て打ち消しあい、奇数の時のみ余った1つ分を考慮すれば良い。
色1 ○○....○○
色2 ○○....○○○○....○○
色3 ○○....○○○○....○○○○○
`--------' `----------'`--'
偶数個=打ち消し合う
↓
色1 ○
色2 ○
色3 ○ ○
ただ、解説の解法は「全ての色で個数が偶数個なら後手、それ以外は先手必勝」となっていて、たしかに解説の証明の通りなので、上の解法はどこかおかしい。
import sys
from collections import Counter
n = int(input())
aaa = [int(line) for line in sys.stdin.readlines()]
cnt = Counter(aaa)
nim = 0
mnt = 0
prev_i = 0
for i, c in sorted(cnt.items()):
di = i - prev_i
mnt += c
if di % 2 == 1:
nim ^= mnt
prev_i = i
print('first' if nim > 0 else 'second')
E - Negative Doubling
問題
- $N$ 個の正整数列 $a_1,a_2,...,a_N$ がある
- 以下の操作を繰り返し行って、(広義)単調増加にしたい
- 数を1つ選んで、「-2」をかける
- 必要な最小操作回数を求めよ
解法

