HUPC 2019 Day1 F: グリッドの番号 (Grid Number)
解法
1から2nまでの数を順番にグリッドに詰めていく。このとき、各状態からの遷移は上段に詰めるか下段に詰めるかの2通りの遷移がある。
各状態を、「下段より右側に出ている上段の数の列に、今まで詰めた数の何個前の数字が入っているか」のビット列として表す。
例えば下の例では、4まで詰めて、上段で下段よりも飛び出している部分は2 4 5、すなわち3個前の数と1個前の数と0個前の数が入っているので、状態は1011と表せる。
1 2 4 5 3
dp[i][state] をiまで詰めて、stateになっている時の組み合わせの数、として持っておけばシンプルな動的計画法で解ける。
コード
fn main() { let s = std::io::stdin(); let mut sc = Scanner { stdin: s.lock() }; let n: usize = sc.read(); let k: usize = sc.read(); let modulo: usize = sc.read(); let mut dp = vec![vec![0; 1 << k]; 2 * n + 1]; dp[0][0] = 1; for i in 0..(2 * n) { for mask in 0..(1 << k) { if mask == (1 << k) - 1 { continue; } if mask & (1 << (k - 1)) == 0 { let next = (mask << 1) | 1; dp[i + 1][next] += dp[i][mask]; dp[i + 1][next] %= modulo; } if mask > 0 { let next = (mask - mask.highest_one_bit()) << 1; dp[i + 1][next] += dp[i][mask]; dp[i + 1][next] %= modulo; } } } let mut ans = dp[2 * n][0]; if n == k { ans += 1; } println!("{}", ans % modulo); } trait HighestOneBit { fn highest_one_bit(self) -> usize; } impl HighestOneBit for usize { fn highest_one_bit(self) -> usize { (self + 1).next_power_of_two() >> 1 } } pub struct Scanner<R> { stdin: R, } impl<R: std::io::Read> Scanner<R> { pub fn read<T: std::str::FromStr>(&mut self) -> T { use std::io::Read; let buf = self .stdin .by_ref() .bytes() .map(|b| b.unwrap()) .skip_while(|&b| b == b' ' || b == b'\n') .take_while(|&b| b != b' ' && b != b'\n') .collect::<Vec<_>>(); unsafe { std::str::from_utf8_unchecked(&buf) } .parse() .ok() .expect("Parse error.") } pub fn vec<T: std::str::FromStr>(&mut self, n: usize) -> Vec<T> { (0..n).map(|_| self.read()).collect() } pub fn chars(&mut self) -> Vec<char> { self.read::<String>().chars().collect() } }