C - Not Too Close SoundHound Programming Contest 2018 Masters Tournament 本戦 (Open)
解法
解説が全く理解できなかったので下記ブログを参考にした。ありがとうございます。
http://fluffyowl.hatenablog.com/entry/2018/07/31/000800
dp[d][i][j] := 頂点 1 からの距離が d となる頂点までグラフを構築したとき、グラフの頂点数が i で、その中で頂点 1 からの距離が d となる頂点の個数が j であるグラフの組み合わせ
を考える。
距離 d-1 まで構築し終えたとして、グラフ全体の頂点数が used 個、頂点 1 からの距離が d-1 の頂点の数が last 個だとする。
このとき使われていない頂点の数 u は
と表される。(距離が D 以下なら使われていない頂点2は使われていないが、そうでない場合は使われているため。)
また、距離 d の頂点として新たにグラフに使用する頂点の数を using 個とすると、新たにグラフに使用する頂点を選ぶ組み合わせ C は
と表される。 (d=D のときは 2 を必ずグラフに使わなければならないため。)
新たにグラフに使用される using 個の頂点は距離 d となるので、 last 個の頂点のいずれかから辺が張られているはずである。よって using 個の各頂点について、 last 個の頂点への辺の貼り方はそれぞれ 通りあるはずである。 (last 個の頂点のどれに辺を張っても良いが、必ず 1 本は張らなければならないため。)なので last 個の頂点と using この頂点のみからなるサブグラフの組み合わせ E は
通りになる。
新たに追加される using この頂点の間では好きなだけ辺を張って良い。(これら using 個の頂点への頂点 1 からの最短距離は d で変わらないので。)貼りうる辺の本数は なので、組み合わせは
通りになる。
よって dp の更新式は
みたいな感じになる。(雰囲気で式を書いているので数学的な表記じゃないのは許してください)
コード
use std::ops::{Add, AddAssign, Mul, MulAssign, Rem, Sub}; const MOD: usize = 1e9 as usize + 7; fn main() { let mut sc = Scanner::new(); let n: usize = sc.read(); let d: usize = sc.read(); let combination = Combination::new(n, MOD); let mut dp = vec![vec![vec![ModInt::new(0); n + 1]; n + 1]; n]; dp[0][1][1] = ModInt::new(1); for distance in 1..n { for using in 1..(n + 1) { for used in 1..(n + 1) { if using + used > n { continue; } for last in 1..(n + 1) { let unused = if distance <= d { n - used - 1 } else { n - used }; let using_not_2 = if distance == d { using - 1 } else { using }; if unused < using_not_2 { continue; } let combination = combination.get(unused, using_not_2); let edges_from_last_to_using = mod_pow((1 << last) - 1, using); let possible_edge_num_in_using_sub_graph = using * (using - 1) / 2; let sub_graphs_of_using = mod_pow(2, possible_edge_num_in_using_sub_graph); dp[distance][using + used][using] += dp[distance - 1][used][last] * combination * edges_from_last_to_using * sub_graphs_of_using; } } } } let mut ans = ModInt::new(0); for distance in d..n { for used in (distance + 1)..(n + 1) { for last in 0..(n + 1) { let unconnected_sub_graph = if used + 1 >= n { ModInt::new(1) } else { mod_pow(2, (n - used) * (n - used - 1) / 2) }; ans += dp[distance][used][last] * unconnected_sub_graph; } } } println!("{}", ans.value); } fn mod_pow(x: usize, e: usize) -> ModInt<usize> { let mut result = ModInt::new(1); let mut cur = ModInt::new(x); let mut e = e; while e > 0 { if e & 1 != 0 { result = result * cur; } e /= 2; cur = cur * cur; } result } #[derive(Copy)] pub struct ModInt<T> { value: T, modulo: T, } impl<T> Clone for ModInt<T> where T: Copy, { fn clone(&self) -> Self { ModInt { value: self.value, modulo: self.modulo, } } fn clone_from(&mut self, source: &ModInt<T>) { self.value = source.value; self.modulo = source.modulo; } } impl<T> Add<ModInt<T>> for ModInt<T> where T: Add<Output = T> + Sub<Output = T> + Copy + PartialOrd, { type Output = ModInt<T>; fn add(self, rhs: ModInt<T>) -> ModInt<T> { self + rhs.value } } impl<T> Add<T> for ModInt<T> where T: Add<Output = T> + Sub<Output = T> + Copy + PartialOrd, { type Output = ModInt<T>; fn add(self, rhs: T) -> ModInt<T> { let m = self.modulo; let mut t = rhs + self.value; if t > m { t = t - m; } ModInt { value: t, modulo: self.modulo, } } } impl<T> Sub<T> for ModInt<T> where T: PartialOrd + Copy + Add<Output = T> + Sub<Output = T> + Rem<Output = T>, { type Output = ModInt<T>; fn sub(self, rhs: T) -> ModInt<T> { let rhs = if rhs >= self.modulo { rhs % self.modulo } else { rhs }; let value = if self.value < rhs { self.value + self.modulo } else { self.value }; ModInt { value: value - rhs, modulo: self.modulo, } } } impl<T> Sub<ModInt<T>> for ModInt<T> where T: PartialOrd + Copy + Add<Output = T> + Sub<Output = T> + Rem<Output = T>, { type Output = ModInt<T>; fn sub(self, rhs: ModInt<T>) -> ModInt<T> { self - rhs.value } } impl<T> AddAssign<T> for ModInt<T> where T: Add<Output = T> + Sub<Output = T> + Copy + PartialOrd, { fn add_assign(&mut self, other: T) { *self = *self + other; } } impl<T> AddAssign<ModInt<T>> for ModInt<T> where T: Add<Output = T> + Sub<Output = T> + Copy + PartialOrd, { fn add_assign(&mut self, other: ModInt<T>) { *self = *self + other; } } impl<T> Mul<ModInt<T>> for ModInt<T> where T: Mul<Output = T> + Rem<Output = T> + Copy, { type Output = ModInt<T>; fn mul(self, rhs: ModInt<T>) -> ModInt<T> { self * rhs.value } } impl<T> Mul<T> for ModInt<T> where T: Mul<Output = T> + Rem<Output = T> + Copy, { type Output = ModInt<T>; fn mul(self, rhs: T) -> ModInt<T> { let t = (self.value * rhs) % self.modulo; ModInt { value: t, modulo: self.modulo, } } } impl<T> MulAssign<T> for ModInt<T> where T: Mul<Output = T> + Rem<Output = T> + Copy, { fn mul_assign(&mut self, rhs: T) { *self = *self * rhs; } } impl<T> MulAssign<ModInt<T>> for ModInt<T> where T: Mul<Output = T> + Rem<Output = T> + Copy, { fn mul_assign(&mut self, rhs: ModInt<T>) { *self = *self * rhs; } } impl ModInt<usize> { pub fn new(value: usize) -> ModInt<usize> { ModInt { value: value, modulo: MOD, } } } pub struct Combination { fact: Vec<usize>, inv_fact: Vec<usize>, modulo: usize, } impl Combination { pub fn new(max: usize, modulo: usize) -> Combination { let mut inv = vec![0; max + 1]; let mut fact = vec![0; max + 1]; let mut inv_fact = vec![0; max + 1]; inv[1] = 1; for i in 2..(max + 1) { inv[i] = inv[modulo % i] * (modulo - modulo / i) % modulo; } fact[0] = 1; inv_fact[0] = 1; for i in 0..max { fact[i + 1] = fact[i] * (i + 1) % modulo; } for i in 0..max { inv_fact[i + 1] = inv_fact[i] * inv[i + 1] % modulo; } Combination { fact: fact, inv_fact: inv_fact, modulo: modulo, } } pub fn get(&self, x: usize, y: usize) -> ModInt<usize> { assert!(x >= y); ModInt::new( self.fact[x] * self.inv_fact[y] % self.modulo * self.inv_fact[x - y] % self.modulo, ) } } struct Scanner { ptr: usize, length: usize, buf: Vec<u8>, small_cache: Vec<u8>, } #[allow(dead_code)] impl Scanner { fn new() -> Scanner { Scanner { ptr: 0, length: 0, buf: vec![0; 1024], small_cache: vec![0; 1024], } } fn load(&mut self) { use std::io::Read; let mut s = std::io::stdin(); self.length = s.read(&mut self.buf).unwrap(); } fn byte(&mut self) -> u8 { if self.ptr >= self.length { self.ptr = 0; self.load(); if self.length == 0 { self.buf[0] = b'\n'; self.length = 1; } } self.ptr += 1; return self.buf[self.ptr - 1]; } fn is_space(b: u8) -> bool { b == b'\n' || b == b'\r' || b == b'\t' || b == b' ' } fn read_vec<T>(&mut self, n: usize) -> Vec<T> where T: std::str::FromStr, T::Err: std::fmt::Debug, { (0..n).map(|_| self.read()).collect() } fn usize_read(&mut self) -> usize { self.read() } fn read<T>(&mut self) -> T where T: std::str::FromStr, T::Err: std::fmt::Debug, { let mut b = self.byte(); while Scanner::is_space(b) { b = self.byte(); } for pos in 0..self.small_cache.len() { self.small_cache[pos] = b; b = self.byte(); if Scanner::is_space(b) { return String::from_utf8_lossy(&self.small_cache[0..(pos + 1)]) .parse() .unwrap(); } } let mut v = self.small_cache.clone(); while !Scanner::is_space(b) { v.push(b); b = self.byte(); } return String::from_utf8_lossy(&v).parse().unwrap(); } }