結果
問題 | No.2067 ±2^k operations |
ユーザー |
|
提出日時 | 2022-09-02 23:13:47 |
言語 | Rust (1.83.0 + proconio) |
結果 |
AC
|
実行時間 | 485 ms / 2,000 ms |
コード長 | 9,551 bytes |
コンパイル時間 | 15,178 ms |
コンパイル使用メモリ | 399,436 KB |
実行使用メモリ | 6,820 KB |
最終ジャッジ日時 | 2024-11-16 06:06:47 |
合計ジャッジ時間 | 24,439 ms |
ジャッジサーバーID (参考情報) |
judge3 / judge4 |
(要ログイン)
ファイルパターン | 結果 |
---|---|
sample | AC * 1 |
other | AC * 23 |
ソースコード
fn read_line(stdin: &mut std::io::Stdin) -> String {let mut str = String::new();stdin.read_line(&mut str).unwrap();str}/// 例: 桁DP/// ## 問題文/// 正整数 L, R, M が与えられます。 L, R は共に 10 進法表現の長さが N の整数です。/// L, R をそれぞれ M/N 回繰り返したものを L', R' とします。/// L' 以上 R' 以下の整数の総和を求めてください。fn main() {let digits = &[0, 1];let mut stdin = std::io::stdin();let t = read_line(&mut stdin).trim().parse::<usize>().unwrap();for _ in 0 .. t {let mut n = read_line(&mut stdin).trim().parse::<i64>().unwrap();let mut ub = vec![];while n > 0 {ub.push(n % 2);n /= 2;}for _ in ub.len() .. 60 {ub.push(0);}ub.reverse();let automaton = AutomatonDP::digit_lte(digits, &ub);let ans = automaton.compute::<_, _, _, _, _>(ub.len(),|(ax, a0, a1, a11, a110), (bx, b0, b1, b11, b110)| (ax + bx, a0 + b0, a1 + b1, a11 + b11, a110 + b110),|| (0i64, 0i64, 0i64, 0i64, 0i64),|&(x, s0, s1, s11, s110), c| {if c == 0 {(x, s0 + s1 + s110, 0, 0, s11)} else {(x + s0 + s1 + s110, 0, s0, s1 + s11 + s110, 0)}},|| (0, 1, 0, 0, 0));println!("{}", ans.0);}}use std::collections::*;use std::hash::Hash;/// オートマトン(DFA)が受理するすべての文字列に対して DP をする/// - `Q`: 状態の型/// - `C`: 入力の型pub struct AutomatonDP<Q, C> {transition: HashMap<Q, HashMap<C, Q>>,init: Q,accept: Vec<Q>,}impl<Q, C> AutomatonDP<Q, C>whereQ: Copy + Eq + Hash,C: Copy + Eq + Hash{/// 初期状態が `init` のオートマトンを作成するpub fn new(init: Q) -> Self {Self {transition: HashMap::new(),init,accept: Vec::new(),}}/// 遷移を追加する/// `from` 状態のとき、 `input` が来ると `to` 状態に遷移するpub fn add_transition(&mut self, from: Q, input: C, to: Q) {self.transition.entry(from).or_insert_with(HashMap::new).insert(input, to);}/// 遷移を削除するpub fn remove_transition(&mut self, from: Q, input: C) {self.transition.entry(from).and_modify(|tr| {tr.remove(&input);});}/// 受理状態を追加するpub fn accept(&mut self, state: Q) {self.accept.push(state);}/// 受理状態を削除する/// O(accept.len())pub fn reject(&mut self, state: Q) {let mut prev = 0;while let Some(index) = (prev .. self.accept.len()).find(|&i| self.accept[i] == state) {self.accept.swap_remove(index);prev = index;}}/// 受理状態をクリアするpub fn reject_all(&mut self) {self.accept.clear();}/// 状態を変換するpub fn map<R, Map>(&self, map: Map) -> AutomatonDP<R, C>whereR: Copy + Eq + Hash,Map: Fn(Q) -> R,{let mut transition = HashMap::new();for (&p, tr) in &self.transition {let mut tr2 = HashMap::new();for (&c, &q) in tr {tr2.insert(c, (map)(q));}transition.insert((map)(p), tr2);}let accept = self.accept.iter().map(|&q| (map)(q)).collect();let init = (map)(self.init);AutomatonDP { transition, accept, init }}/// 長さ `n` のすべての文字列に対して DP をする/// - `op`: 加法/// - `e`: 加法単位元を返す/// - `map`: 乗法っぽいやつ/// - `empty`: 空列に対する結果を返す////// ただし、乗せる値は半環(っぽいやつ)でないといけない/// つまり:/// ```/// assert_eq!((op)(x, (op)(y, z)), (op)((op)(x, y), z));/// assert_eq!((op)(x, (e)()), x);/// assert_eq!((op)((e)(), x), x);/// assert_eq!((op)(x, y), (op)(y, x));/// assert_eq!((map)(c, (op)(x, y)), (op)((map)(c, x), (map)(c, y)));/// ```/// 計算量: O(n (状態数 + 遷移数))pub fn compute<S, Op, E, Map, Empty>(&self, n: usize, op: Op, e: E, map: Map, empty: Empty) -> SwhereS: Clone + Sized,Op: Fn(&S, &S) -> S,E: Fn() -> S,Map: Fn(&S, C) -> S,Empty: Fn() -> S,{let mut dp = HashMap::new();dp.insert(self.init, (empty)());for _ in 0 .. n {dp = self.dp_forward(&dp, &op, &e, &map);}let mut ans = (e)();for &q in &self.accept {ans = (op)(&ans, &dp.get(&q).cloned().unwrap_or_else(|| (e)()));}ans}pub fn dp_forward<S, Op, E, Map>(&self, dp: &HashMap<Q, S>, op: &Op, e: &E, map: &Map) -> HashMap<Q, S>whereS: Clone + Sized,Op: Fn(&S, &S) -> S,E: Fn() -> S,Map: Fn(&S, C) -> S,{let mut dp2 = HashMap::new();for (&from, value) in dp {for (&input, &to) in &self.transition[&from] {let x = dp2.entry(to).or_insert_with(|| (e)());let y = (op)(&x, &(map)(value, input));*x = y;}}dp2}/// 論理積をとるpub fn intersection<R>(&self, other: &AutomatonDP<R, C>) -> AutomatonDP<(Q, R), C>whereR: Copy + Eq + Hash{let mut transition = HashMap::new();for (&p1, tr1) in &self.transition {for (&c, &q1) in tr1 {for (&p2, tr2) in &other.transition {if let Some(&q2) = tr2.get(&c) {transition.entry((p1, p2)).or_insert_with(HashMap::new).insert(c, (q1, q2));}}}}let mut accept = Vec::new();for &q1 in &self.accept {for &q2 in &other.accept {accept.push((q1, q2));}}AutomatonDP {init: (self.init, other.init),transition,accept,}}}#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]pub enum DigitDPState {BothBounded(usize),LowerBounded(usize),UpperBounded(usize),Unbounded(usize),}impl<C> AutomatonDP<DigitDPState, C>whereC: Copy + Eq + Hash{/// 辞書順で upper_bound 以下の数列を受理するオートマトンを作成/// 頂点数 O(upper_bound.len())/// 辺数 O(upper_bound.len() * digits.len())pub fn digit_lte(digits: &[C], upper_bound: &[C]) -> Self {use DigitDPState::*;let n = upper_bound.len();let mut automaton = AutomatonDP::new(UpperBounded(0));automaton.accept(UpperBounded(n));automaton.accept(Unbounded(n));for i in 0 ..= n {let r = upper_bound[i % n];for &c in digits {automaton.add_transition(Unbounded(i), c, Unbounded(i % n + 1));}for &c in digits {if c == r {automaton.add_transition(UpperBounded(i), c, UpperBounded(i % n + 1));break;}automaton.add_transition(UpperBounded(i), c, Unbounded(i % n + 1));}}automaton}/// 辞書順で lower_bound 以上の数列を受理するオートマトンを作成/// 頂点数 O(lower_bound.len())/// 辺数 O(lower_bound.len() * digits.len())pub fn digit_gte(digits: &[C], lower_bound: &[C]) -> Self {use DigitDPState::*;let n = lower_bound.len();let mut automaton = AutomatonDP::new(LowerBounded(0));automaton.accept(LowerBounded(n));automaton.accept(Unbounded(n));for i in 0 ..= n {let l = lower_bound[i % n];for &c in digits {automaton.add_transition(Unbounded(i), c, Unbounded(i % n + 1));}for &c in digits.into_iter().rev() {if c == l {automaton.add_transition(LowerBounded(i), c, LowerBounded(i % n + 1));break;}automaton.add_transition(LowerBounded(i), c, Unbounded(i % n + 1));}}automaton}/// 辞書順で lower_bound 以上 upper_bound 以下の数列を受理するオートマトンを作成pub fn digit_between(digits: &[C], lower_bound: &[C], upper_bound: &[C]) -> Self {use DigitDPState::*;assert_eq!(upper_bound.len(), lower_bound.len());let n = lower_bound.len();let s = digits.len();let mut automaton = AutomatonDP::new(BothBounded(0));automaton.accept(BothBounded(n));automaton.accept(LowerBounded(n));automaton.accept(UpperBounded(n));automaton.accept(Unbounded(n));for i in 0 ..= n {let (l, r) = (lower_bound[i % n], upper_bound[i % n]);for &c in digits {automaton.add_transition(Unbounded(i), c, Unbounded(i % n + 1));}for &c in digits.into_iter().rev() {if c == l {automaton.add_transition(LowerBounded(i), c, LowerBounded(i % n + 1));break;}automaton.add_transition(LowerBounded(i), c, Unbounded(i % n + 1));}for &c in digits {if c == r {automaton.add_transition(UpperBounded(i), c, UpperBounded(i % n + 1));break;}automaton.add_transition(UpperBounded(i), c, Unbounded(i % n + 1));}let lower = (0 .. s).find(|&j| digits[j] == l).unwrap();let upper = (0 .. s).find(|&j| digits[j] == r).unwrap();if lower == upper {automaton.add_transition(BothBounded(i), digits[lower], BothBounded(i % n + 1));} else if lower < upper {automaton.add_transition(BothBounded(i), digits[lower], LowerBounded(i % n + 1));automaton.add_transition(BothBounded(i), digits[upper], UpperBounded(i % n + 1));for &c in &digits[lower + 1 .. upper] {automaton.add_transition(BothBounded(i), c, Unbounded(i % n + 1));}}}automaton}}