結果
| 問題 |
No.3047 Verification of Sorting Network
|
| ユーザー |
👑 |
| 提出日時 | 2025-02-15 00:51:51 |
| 言語 | Rust (1.83.0 + proconio) |
| 結果 |
AC
|
| 実行時間 | 265 ms / 2,000 ms |
| コード長 | 8,067 bytes |
| コンパイル時間 | 18,523 ms |
| コンパイル使用メモリ | 398,208 KB |
| 実行使用メモリ | 8,608 KB |
| 最終ジャッジ日時 | 2025-03-09 20:11:55 |
| 合計ジャッジ時間 | 28,984 ms |
|
ジャッジサーバーID (参考情報) |
judge5 / judge4 |
(要ログイン)
| ファイルパターン | 結果 |
|---|---|
| sample | AC * 3 |
| other | AC * 61 |
ソースコード
const MAX_T: usize = 1000;
const MAX_N: usize = 27;
const MAX_COST: f64 = 1e8;
type NodeIndex = u8;
type CmpIndex = usize;
type State = u64;
// 与えられた比較交換器ネットワークがソーティングネットワークかどうかをチェック
pub fn is_sorting_network(
n: usize,
cmp: &[(NodeIndex, NodeIndex)],
) -> Result<Vec<bool>, [bool; State::BITS as usize]> {
const { assert!(MAX_N <= NodeIndex::MAX as _ && MAX_N * (MAX_N - 1) / 2 <= CmpIndex::MAX as _) };
debug_assert!(2 <= n && (n as usize) <= MAX_N && n <= State::BITS as _);
debug_assert!(cmp.len() <= CmpIndex::MAX as _);
// 0-indexed かつ a < b かつ b < n であることを確認
let _n = n as NodeIndex;
debug_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
let mut stack = Vec::<(State, State, CmpIndex)>::with_capacity(n as _);
stack.push((State::MAX >> (State::BITS - n as u32), State::MAX, 0));
// unused[i]: i番目の要素がソーティングネットワークで使用されているか
let mut unused = vec![true; cmp.len()];
// unsorted[i]: i,(i+1)番目の要素ペアがソートされていない可能性があるか
let mut unsorted = [false; State::BITS as usize];
// 非再帰の深さ優先探索による分岐状態の探索
while let Some((mut z, mut o, i)) = stack.pop() {
// 比較交換器を順に適用
// 0,1状態ベクトル z,o の a,b-bit目が比較交換器の入出力
if let Some(&(a, b)) = cmp.get(i as usize) {
if (o >> a) & 1 == 0 || (z >> b) & 1 == 0 {
// 比較交換器の入力が ('0', _) or (_, '1') の場合、交換はないので次の比較交換器へ進む
stack.push((z, o, i + 1));
} else if (z >> a) & 1 != 0 && (o >> b) & 1 != 0 {
// この比較交換器は使用される可能性あり
unused[i as usize] = false;
// 比較交換器の入力が ('?', '?') であれば、 ('0', '0') と ('?', '1') に分岐して探索
// 新しい分岐 (qz,qo) は ('?', '?') -> ('0', '0') に遷移
let (qz, mut qo) = (z, o);
qo &= !(1 << a) & !(1 << b);
// 現在の分岐 (z,o) は ('?', '?') -> ('?', '1') に遷移
z &= !(1 << b);
// (qz,qo) 分岐をスタックに積む
stack.push((qz, qo, i + 1));
// (z,o) 分岐をスタックに積む
stack.push((z, o, i + 1));
} else {
// この比較交換器は使用される可能性あり
unused[i as usize] = false;
// 比較交換器の入力が ('1', '0') or ('?', '0') or ('1', '?') の場合、要素を交換
let xz = ((z >> a) ^ (z >> b)) & 1;
let xo = ((o >> a) ^ (o >> b)) & 1;
z ^= xz << a | xz << b;
o ^= xo << a | xo << b;
// 次の比較交換器へ進む
stack.push((z, o, i + 1));
}
} else if (z & !o).trailing_ones() + (o & !z).leading_ones() < State::BITS - 1 {
// この分岐で全ての比較交換が完了し、z,o がソートされていない場合
// ソートされていない位置をマーク
let mut np = o & (z >> 1);
while np != 0 {
let j = np.trailing_zeros() as usize;
unsorted[j] = true;
np &= np - 1;
}
}
}
// いずれかの分岐でソートされていない場合、 unsorted に true が含まれるのでソーティングネットワークではない
if unsorted.iter().any(|&f| f) {
// ソートされていない可能性のある位置を返す
Err(unsorted)
} else {
// すべての分岐においてソートされたならばソーティングネットワーク
// 未使用の比較交換器を返す
Ok(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<_>>();
assert!(vec_a.len() == m && vec_b.len() == m);
assert!(vec_a.iter().all(|&a| 1 <= a && a <= n));
assert!(vec_b.iter().all(|&b| 1 <= b && b <= n));
let cmp = vec_a
.iter()
.zip(vec_b.iter())
.map(|(&a, &b)| ((a - 1) as NodeIndex, (b - 1) as NodeIndex))
.collect::<Vec<_>>();
assert!(cmp.len() == m);
assert!(cmp.iter().all(|&(a, b)| a < b));
// ソーティングネットワークかどうかをチェック
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 を列挙
let unsorted_adjacent = unsorted.iter().enumerate()
.filter_map(|(k, &f)| if f { Some(k) } else { None })
.collect::<Vec<_>>();
writeln!(&mut bout, "{}", unsorted_adjacent.len())?;
// 1-indexed
writeln!(
&mut bout,
"{}",
unsorted_adjacent
.iter()
.map(|k| (k + 1).to_string())
.collect::<Vec<_>>()
.join(" ")
)?;
}
}
}
bout.flush()?;
eprintln!("{} ms", execution_start.elapsed().as_millis());
Ok(())
}