Как и все остальные, я вычислил функцию, которую я теперь знаю, это Rencontres Numbers, но я сам вывел рекурсивное уравнение в конкурсе. Без потери общности мы просто предполагаем, что правильные метки вина 1, 2, .., g
, т.е. не переставляются вообще.
Обозначим функцию как f(g,c)
. Учитывая g очков, мы смотрим на первый стакан, и мы могли бы или маркировать это правильно, или маркировать это неправильно.
- Если мы помечаем это правильно, мы уменьшаем проблему до получения
c-1
прямо из g-1
очков, то есть f(g-1, c-1)
.
- Если мы пометим его неправильно, у нас будет
g-1
выбор для первого стакана. Для остальных очков g-1
мы должны получить правильные очки c
, но эта подзадача отличается от f
, которую мы вычисляем, потому что из очков g-1
уже есть несоответствующее стекло. Точнее, для первого стекла наш ответ j
вместо правильной метки 1
. Давайте предположим, что есть другая функция h
, которая вычисляет ее для нас.
Итак, у нас есть f(g,c) = f(g-1,c-1) + (g-1) * h(g-1, c)
.
Теперь, чтобы вычислить h(g,c)
, нам нужно рассмотреть два случая у j
-го стекла.
- Если мы пометим его
1
, мы уменьшим проблему до f(g-1,c)
.
- Если мы пометим его
k
, у нас будет g-1
вариантов, и проблема уменьшится до h(g-1,c)
.
Итак, у нас есть h(g,c) = f(g-1,c) + (g-1) * h(g-1,c)
.
Вот полная программа на Хаскелле, с памяткой и некоторой поддержкой отладки.
import Control.Monad
import Data.MemoTrie
--import Debug.Trace
trace = flip const
add a b = mod (a+b) 1051962371
mul a b = mod (a*b) 1051962371
main = do
(_:input) <- liftM words getContents
let map' f [] = []
map' f (a:c:xs) = f (read a) (read c) : map' f xs
mapM print $ map' ans input
ans :: Integer -> Integer -> Integer
ans g c = foldr add 0 $ map (f g) [c..g]
memoF = memo2 f
memoH = memo2 h
-- Exactly c correct in g
f :: Integer -> Integer -> Integer
f g c = trace ("f " ++ show (g,c) ++ " = " ++ show x) x
where x = if c < 0 || g < c then 0
else if g == c then 1
else add (memoF (g-1) (c-1)) (mul (g-1) (memoH (g-1) c))
-- There's one mismatching position in g positions
h :: Integer -> Integer -> Integer
h g c = trace ("h " ++ show (g,c) ++ " = " ++ show x) x
where x = if c < 0 || g < c then 0
else add (memoF (g-1) c) (mul (g-1) (memoH (g-1) c))