Top
CodeIQ No.2628
2つの正整数 $(a, b)$ が与えられたとき、
等式 $x^{2} + a^{2} = y^{2} + b^{2}$ を満たす正整数解 $(x, y)$ の全てについて、$x+y$ の総和を関数 $f(a, b)$ とします。
$f(a, b)$ を求めてください。
・$1 \le a \lt b \le 10^{5}$

$x^{2} + a^{2} = y^{2} + b^{2}$ となる $(x, y)$ を求めたい。
変数と定数に分離すると、
$x^{2} - y^{2} = b^{2} - a^{2}$
左辺を因数分解して、
$(x+y)(x-y) = b^{2} - a^{2}$

ここで $b^{2} - a^{2}$ を二正整数( $p,q$ とする。$p \lt q$ )の積で計算することができれば、
$\begin{aligned} \left\{ \begin{array}{l} x+y = q \\ x-y = p \end{array} \right. \end{aligned}$ なので、$x$ と $y$ を以下のように求めることができる。
$\begin{aligned} \left\{ \begin{array}{l} \displaystyle x = \frac{q+p}{2} \\ \\ \displaystyle y = \frac{q-p}{2} \end{array} \right. \end{aligned}$
ここで、$x$ や $y$ は $0$ であってはならない(つまり $p=q$ であってはならない)ことに注意すること。

(ΦωΦ)<ソース
#!/usr/bin/python3
def divisors(n):
    sq = int(n ** .5)
    res = ((i, n//i) for i in range(1, sq+1) if n%i==0)
    return res

def f(a, b):
    res = 0
    pq = b**2 - a**2
    for p, q in divisors(pq):
        if p == q:
            continue
        if (q + p) & 1 or (q - p) & 1:
            continue
        res += q # x+y=q
    return res

###
from collections import namedtuple
import unittest

T = namedtuple('T', 'func, inntak, expected')

test_cases = [
    T(f, (3, 10), 104),
    T(f, (10, 50), 3500),
    T(f, (20, 100), 15022),
    T(f, (4, 5), 9),
    ###
    T(f, (10, 26), 716),
    T(f, (11, 389), 291886),
    T(f, (123, 456), 294166),
    T(f, (35672, 61243), 5082405064),
    T(f, (71200, 82321), 2490724704),
    T(f, (19126, 98765), 16043311668),
]

class TestSequence(unittest.TestCase):
    pass

def test_generator(func, inntak, expected):
    def _test(self):
        self.assertEqual(func(*inntak), expected)
    return _test

for case in test_cases:
    name = 'test-{}-{}'.format(case.func.__name__, case.inntak)
    test = test_generator(*case)
    setattr(TestSequence, name, test)
suite = unittest.TestLoader().loadTestsFromTestCase(TestSequence)
testresult = unittest.TextTestRunner().run(suite)
assert testresult.wasSuccessful()
###

a, b = map(int, input().split())
res = f(a, b)
print(res)
    

---
$a$ や $b$ がもっと大きな値だったとする。
仮に $1 \le a \lt b \le 10^{10}$ だったとする。
この場合、上のソースの関数divisors()が遅いので、返事がなかなか返ってこなくなる。

そこで、divisors()をもっと効率的に実装する必要がある。
ひとつの案として、
「素因数分解した結果の素数の集合を全集合とし、それを2つの部分集合に分ける」 というのが考えられる。

このとき、素因数分解するのは $b^{2} - a^{2}$ であることに注目すると、
$b+a$ と $b-a$ のそれぞれを素因数分解し、それらの結果をマージすれば、$b^{2} - a^{2}$ を素因数分解するよりも計算量はぐっと小さくできる。

(ΦωΦ)<ソース
#!/usr/bin/python3
# -*- coding: utf-8 -*-
# †
from collections import defaultdict
from functools import reduce
from itertools import product
from operator import mul

def factorize(n):
    res = defaultdict(int)
    d = 2
    while d * d <= n:
        while n % d == 0:
            res[d] += 1
            n //= d
        d += 1
    if n > 1:
        res[n] += 1
    return res

def merge(*dd):
    res = defaultdict(int)
    for d in dd:
        for k, v in d.items():
            res[k] += v
    return res

def gen_divisors(a, b):
    n = b*b - a*a
    factors = merge(factorize(b+a), factorize(b-a))
    keys = factors.keys()   # 素数
    vals = factors.values() # 冪乗(個数)
    for p in product(*(range(v+1) for v in vals)):
        res = reduce(mul, (x**y for x, y in zip(keys, p)))
        if res < n//res:    # 結局、関数fで等号時を弾くので、ここで弾いておく
            yield res, n//res

def f(a, b):
    res = 0
    for p, q in gen_divisors(a, b):
        if (q + p) & 1 or (q - p) & 1:
            continue
        res += q
    return res

###
from collections import namedtuple
import unittest

T = namedtuple('T', 'func, inntak, expected')

test_cases = [
    T(f, (3, 10), 104),
    T(f, (10, 50), 3500),
    T(f, (20, 100), 15022),
    T(f, (4, 5), 9),
    ###
    T(f, (10, 26), 716),
    T(f, (11, 389), 291886),
    T(f, (123, 456), 294166),
    T(f, (35672, 61243), 5082405064),
    T(f, (71200, 82321), 2490724704),
    T(f, (19126, 98765), 16043311668),
    ###
    T(f, (1, 10**10), 163688880842463429502),
    T(f, (9, 10**10), 105925721330425759518),
    T(f, (7,  9999999936), 99999998730000003990),
    T(f, (12, 9999999955), 99999999110000001848),
    T(f, (16, 9999999017), 133333307160001284080),
    T(f, (11, 9998000141), 169344028062100814926),
    T(f, (2,  9998000059), 157233566772634607620),
    T(f, (2,  9998000147), 198016019985396859584),
    T(f, (13, 9998000179), 163570929954618401814),
    T(f, (16, 9998000263), 144820388526916915560),
    T(f, (8,  9999400031), 149765240169422529784),
    T(f, (12, 9999400031), 114272001199952686992),
    T(f, (14, 9999400031), 171980104323475531012),
    T(f, (11, 9998000057), 127427336246752144960),
    T(f, (21, 9998000057), 81788730691229073300),
]

class TestSequence(unittest.TestCase):
    pass

def test_generator(func, inntak, expected):
    def _test(self):
        self.assertEqual(func(*inntak), expected)
    return _test

for case in test_cases:
    name = 'test-{}-{}'.format(case.func.__name__, case.inntak)
    test = test_generator(*case)
    setattr(TestSequence, name, test)
suite = unittest.TestLoader().loadTestsFromTestCase(TestSequence)
testresult = unittest.TextTestRunner().run(suite)
assert testresult.wasSuccessful()
###

a, b = map(int, input().split())
res = f(a, b)
print(res)
    

(ΦωΦ)<おしまい