結果

問題 No.3047 Verification of Sorting Network
ユーザー 👑 Mizar
提出日時 2025-02-12 11:56:13
言語 Rust
(1.83.0 + proconio)
結果
AC  
実行時間 418 ms / 2,000 ms
コード長 10,021 bytes
コンパイル時間 13,566 ms
コンパイル使用メモリ 397,228 KB
実行使用メモリ 7,324 KB
最終ジャッジ日時 2025-03-09 23:56:03
合計ジャッジ時間 28,254 ms
ジャッジサーバーID
(参考情報)
judge1 / judge4
このコードへのチャレンジ
(要ログイン)
ファイルパターン 結果
sample AC * 3
other AC * 61
権限があれば一括ダウンロードができます

ソースコード

diff #

const MAX_T: usize = 10000;
const MAX_N: usize = 27;
const MAX_COST: f64 = 1e8;
//const MAX_T: usize = 100000;
//const MAX_N: usize = 64;
//const MAX_COST: f64 = 1e11;

type NodeIndex = u8;
type CmpIndex = usize;
type State = u64;

const FIB1: [State; State::BITS as usize + 1] = {
    let mut fib = [0; State::BITS as usize + 1];
    fib[0] = 1;
    fib[1] = 1;
    let mut i = 2;
    while i <= State::BITS as _ {
        fib[i] = fib[i - 1] + fib[i - 2];
        i += 1;
    }
    fib
};

// 与えられた比較交換器ネットワークがソーティングネットワークかどうかをチェック
fn is_sorting_network(n: usize, cmp: &[(usize, usize)]) -> Result<Vec<bool>, Vec<bool>> {
    const { assert!(MAX_N <= NodeIndex::MAX as _ && MAX_N * (MAX_N - 1) / 2 <= CmpIndex::MAX as _) };
    assert!(2 <= n && (n as usize) <= MAX_N && n <= State::BITS as _ && n <= NodeIndex::MAX as _);
    assert!(cmp.len() <= CmpIndex::MAX as _);
    // 0-indexed かつ a < b かつ b < n であることを確認
    assert!(cmp.iter().all(|&(a, b)| a < b && b < n));
    // スタックに積む要素: 0状態ベクトル z, 1状態ベクトル o, 比較交換器の番号 i
    // もし ((z >> j) & 1, (o >> j) & 1) == (1, 1) なら j-bit目の状態は '?'
    // もし ((z >> j) & 1, (o >> j) & 1) == (1, 0) なら j-bit目の状態は '0'
    // もし ((z >> j) & 1, (o >> j) & 1) == (0, 1) なら j-bit目の状態は '1'
    // '0...01...1' または '0...0?1...1' の場合、ソートされている
    // 初期状態: 0状態ベクトルは[0,n)-bit目が1、1状態ベクトルは全bitが1、比較交換器の番号は 0
    struct SearchState {
        n: usize,
        unused: Vec<bool>,
        unsorted: Vec<bool>,
        progress: State,
        next_progress: State,
    }
    impl SearchState {
        fn progress_inc(&mut self, x: State) {
            self.progress += x;
            if self.progress >= self.next_progress {
                let percent = self.progress * 100 / FIB1[self.n];
                eprint!("\r{}%", percent);
                self.next_progress = ((percent + 1) * FIB1[self.n] - 1) / 100 + 1;
            }
        }
        fn search(&mut self, cmp: &[(NodeIndex, NodeIndex)], z: State, o: State, i: usize) {
            if let Some(&(a, b)) = cmp.get(i) {
                match ((z >> a) & 1, (o >> a) & 1, (z >> b) & 1, (o >> b) & 1) {
                    (1, 0, _, _) | (_, _, 0, 1) => {
                        // 比較交換器の入力が ('0', _) or (_, '1') {_ は任意を示す} の場合、
                        // 交換はないので次の比較交換器へ進む
                        self.search(cmp, z, o, i + 1);
                    }
                    (1, 1, 1, 1) => {
                        // この比較交換器は使用される可能性あり
                        self.unused[i] = false;
                        // 比較交換器の入力が ('?', '?') であれば、 ('0', '0') と ('?', '1') に分岐して探索
                        // qz,qo: ('?', '?') -> ('0', '0')
                        let (qz, qo) = (z, o & (!(1 << a) & !(1 << b)));
                        // rz,ro: ('?', '?') -> ('?', '1')
                        let (rz, ro) = (z & !(1 << b), o);
                        // (qz,qo) がソートされていない場合、分岐を調べる
                        if (qz & !qo).trailing_ones() + (qo & !qz).leading_ones() < State::BITS - 1
                        {
                            self.search(cmp, qz, qo, i + 1);
                        } else {
                            self.progress_inc(1);
                        }
                        // (rz,ro) がソートされていない場合、分岐を調べる
                        if (rz & !ro).trailing_ones() + (ro & !rz).leading_ones() < State::BITS - 1
                        {
                            self.search(cmp, rz, ro, i + 1);
                        } else {
                            self.progress_inc(1);
                        }
                    }
                    _ => {
                        // この比較交換器は使用される可能性あり
                        self.unused[i] = false;
                        // 比較交換器の入力が ('1', '0') or ('?', '0') or ('1', '?') の場合、要素を交換
                        let xz = ((z >> a) ^ (z >> b)) & 1;
                        let xo = ((o >> a) ^ (o >> b)) & 1;
                        let qz = z ^ (xz << a | xz << b);
                        let qo = o ^ (xo << a | xo << b);
                        // (rz,ro) がソートされていない場合、分岐を調べる
                        if (qz & !qo).trailing_ones() + (qo & !qz).leading_ones() < State::BITS - 1
                        {
                            self.search(cmp, qz, qo, i + 1);
                        } else {
                            self.progress_inc(1);
                        }
                    }
                }
            } else {
                for j in 0..State::BITS - 1 {
                    match (
                        (z >> j) & 1,
                        (o >> j) & 1,
                        (z >> (j + 1)) & 1,
                        (o >> (j + 1)) & 1,
                    ) {
                        // ('0', '0') または ('1', '1') または ('0', '1') または ('0', '?') または ('?', '1') の場合
                        // 操作なし
                        (1, 0, _, _) | (_, _, 0, 1) => {}
                        // ('?', '?') または ('?', '0') または ('1', '?') または ('1', '0') の場合
                        // ソートされていないとマーク
                        _ => {
                            self.unsorted[j as usize] = true;
                        }
                    }
                }
                self.progress_inc(FIB1[(z & o).count_ones() as usize]);
            }
        }
    }

    let mut state = SearchState {
        n,
        unused: vec![true; cmp.len()],
        unsorted: vec![false; (n - 1) as _],
        progress: 0,
        next_progress: 0,
    };
    state.search(
        &cmp.iter()
            .map(|&(a, b)| (a as NodeIndex, b as NodeIndex))
            .collect::<Vec<_>>(),
        State::MAX >> (State::BITS - n as u32),
        State::MAX,
        0,
    );
    eprintln!();
    assert!(state.progress == FIB1[n]);

    // すべての分岐が終了
    // いずれかの分岐でソートされていない場合、 unsorted に true が含まれるのでソーティングネットワークではない
    if state.unsorted.iter().any(|&f| f) {
        // ソートされていない可能性のある位置を返す
        Err(state.unsorted)
    } else {
        // すべての分岐においてソートされたならばソーティングネットワーク
        // 未使用の比較交換器を返す
        Ok(state.unused)
    }
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
    use std::io::Write;
    let execution_start = std::time::Instant::now();
    let stdin = std::io::stdin();
    let mut lines = std::io::BufRead::lines(stdin.lock());
    let mut bout = std::io::BufWriter::new(std::io::stdout());

    let t: usize = lines.next().unwrap()?.trim().parse()?;
    assert!(t <= MAX_T);

    // φ = (1 + √5) / 2 : 黄金数 1.618033988749895
    let phi = (1.25f64).sqrt() + 0.5;
    let mut cost = 0f64;

    for _ in 0..t {
        let line = lines.next().unwrap()?;
        let mut parts = line.split_whitespace();
        let n: usize = parts.next().unwrap().parse()?;
        let m: usize = parts.next().unwrap().parse()?;
        assert!(2 <= n && (n as usize) <= MAX_N);
        assert!(1 <= m && m <= (n as usize) * ((n as usize) - 1) / 2);
        cost += m as f64 * phi.powi(n as i32);
        // テストケースのコスト <= MAX_COST
        assert!(cost <= MAX_COST);

        // 比較交換器を読み込む
        let vec_a = lines
            .next()
            .unwrap()?
            .split_whitespace()
            .map(|s| s.parse::<usize>().unwrap())
            .collect::<Vec<_>>();
        let vec_b = lines
            .next()
            .unwrap()?
            .split_whitespace()
            .map(|s| s.parse::<usize>().unwrap())
            .collect::<Vec<_>>();
        let cmp = vec_a
            .iter()
            .zip(vec_b.iter())
            .map(|(&a, &b)| (a - 1, b - 1))
            .collect::<Vec<_>>();

        // ソーティングネットワークかどうかをチェック
        match is_sorting_network(n, &cmp) {
            Ok(unused) => {
                writeln!(&mut bout, "Yes")?;
                // 未使用の比較交換器 j を列挙
                writeln!(&mut bout, "{}", unused.iter().filter(|&&f| f).count())?;
                // 1-indexed
                writeln!(
                    &mut bout,
                    "{}",
                    unused
                        .iter()
                        .enumerate()
                        .filter_map(|(j, &u)| if u { Some((j + 1).to_string()) } else { None })
                        .collect::<Vec<_>>()
                        .join(" ")
                )?;
            }
            Err(unsorted) => {
                writeln!(&mut bout, "No")?;
                // ソートされていない可能性がある位置 k を列挙
                writeln!(&mut bout, "{}", unsorted.iter().filter(|&&f| f).count())?;
                // 1-indexed
                writeln!(
                    &mut bout,
                    "{}",
                    unsorted
                        .iter()
                        .enumerate()
                        .filter_map(|(k, &u)| if u { Some((k + 1).to_string()) } else { None })
                        .collect::<Vec<_>>()
                        .join(" ")
                )?;
            }
        }
    }
    bout.flush()?;
    eprintln!("{} ms", execution_start.elapsed().as_millis());
    Ok(())
}
0