結果

問題 No.173 カードゲーム(Medium)
コンテスト
ユーザー cologne
提出日時 2026-02-05 18:04:52
言語 Rust
(1.93.0 + proconio + num + itertools)
結果
AC  
実行時間 1,495 ms / 3,000 ms
コード長 7,579 bytes
記録
記録タグの例:
初AC ショートコード 純ショートコード 純主流ショートコード 最速実行時間
コンパイル時間 3,978 ms
コンパイル使用メモリ 223,288 KB
実行使用メモリ 7,844 KB
最終ジャッジ日時 2026-02-05 18:05:08
合計ジャッジ時間 12,176 ms
ジャッジサーバーID
(参考情報)
judge2 / judge3
このコードへのチャレンジ
(要ログイン)
ファイルパターン 結果
other AC * 10
権限があれば一括ダウンロードができます
コンパイルメッセージ
warning: trait `SmallInt` is never used
 --> src/main.rs:9:15
  |
9 |     pub trait SmallInt {
  |               ^^^^^^^^
  |
  = note: `#[warn(dead_code)]` (part of `#[warn(unused)]`) on by default

warning: methods `sample_range`, `sample_uniform`, and `next_bool` are never used
   --> src/main.rs:93:16
    |
 57 |     impl SplitMix64 {
    |     --------------- methods in this implementation
...
 93 |         pub fn sample_range<T: SmallInt, R: RangeBounds<T>>(&mut self, rng: R) -> T {
    |                ^^^^^^^^^^^^
...
110 |         pub fn sample_uniform(&mut self, rng: Range<f64>) -> f64 {
    |                ^^^^^^^^^^^^^^
...
116 |         pub fn next_bool(&mut self, p_true: f64) -> bool {
    |                ^^^^^^^^^

ソースコード

diff #
raw source code

use fio::*;
use std::cmp::Reverse;

mod rng {
    use std::ops::{
        Bound::{Excluded, Included, Unbounded},
        Range, RangeBounds,
    };
    pub trait SmallInt {
        fn rng_size_u64<T: RangeBounds<Self>>(rng: &T) -> Option<u64>;
        fn pick_nth<T: RangeBounds<Self>>(rng: &T, nth: u64) -> Self;
    }

    macro_rules! small_int {
        ($($SelfT:ty)*) => {
            $(impl SmallInt for $SelfT {
                fn rng_size_u64<T:RangeBounds<Self>>(rng: &T) -> Option<u64> {
                    let start_inclusive = match rng.start_bound() {
                        Included(x) => *x,
                        Excluded(x) => if *x != Self::MAX { x+1 } else {return Some(0)},
                        Unbounded => Self::MIN,
                    };
                    let end_inclusive = match rng.end_bound() {
                        Included(x) => *x,
                        Excluded(x) => if *x != Self::MIN { x-1 } else { return Some(0); },
                        Unbounded => Self::MAX,
                    };
                    if start_inclusive > end_inclusive {
                        Some(0u64)
                    } else {
                        (end_inclusive as u64).wrapping_sub(start_inclusive as u64).checked_add(1)
                    }
                }
                fn pick_nth<T: RangeBounds<Self>>(rng: &T, nth: u64) -> Self {
                    (match rng.start_bound() {
                        Included(x) => ((*x) as u64) + nth,
                        Excluded(x) => ((*x) as u64).wrapping_add(nth).wrapping_sub(1),
                        Unbounded => (Self::MIN as u64) + nth,
                    }) as Self
                }
            })*
        }
    }

    small_int!(i8 i16 i32 i64 u8 u16 u32 u64);
    #[cfg(any(
        target_pointer_width = "16",
        target_pointer_width = "32",
        target_pointer_width = "64"
    ))]
    small_int!(usize isize);

    pub struct SplitMix64 {
        x: u64,
    }

    impl SplitMix64 {
        pub fn new(seed: u64) -> Self {
            Self { x: seed }
        }

        pub fn next(&mut self) -> u64 {
            self.x = self.x.wrapping_add(0x9e3779b97f4a7c15);
            let mut z = self.x;
            z = (z ^ (z >> 30)).wrapping_mul(0xbf58476d1ce4e5b9);
            z = (z ^ (z >> 27)).wrapping_mul(0x94d049bb133111eb);
            z ^ (z >> 31)
        }

        #[cold]
        fn _cold_sample(&mut self, n: u64, mut m: u128, mut l: u64) -> u64 {
            let t = n.wrapping_neg() % n;
            while l < t {
                m = (self.next() as u128) * (n as u128);
                l = m as u64;
            }
            (m >> 64) as u64
        }

        /// Generate number from range from 0..n, i.e. from 0 to n, including 0 and excluding n.
        pub fn sample(&mut self, n: u64) -> u64 {
            assert_ne!(n, 0, "Cannot sample from empty range");
            // Lemire
            let m = (self.next() as u128) * (n as u128);
            let l = m as u64;
            if l < n {
                return self._cold_sample(n, m, l);
            }
            (m >> 64) as u64
        }

        /// Uniformly sample from range
        pub fn sample_range<T: SmallInt, R: RangeBounds<T>>(&mut self, rng: R) -> T {
            let nth = match T::rng_size_u64(&rng) {
                None => self.next(),
                Some(v) => self.sample(v),
            };
            T::pick_nth(&rng, nth)
        }

        /// All values that can be generated are form of n * eps/2, for 0.0..1.0
        pub fn next_f64(&mut self) -> f64 {
            let sig = 1u64 << f64::MANTISSA_DIGITS;
            let val = self.sample(sig) as f64;
            val / sig as f64
        }

        /// Uniformly sample from uniform distribution. Only accept standard Range<f64>.
        /// All values that can be generated are form of n * eps/2, for 0.0..1.0
        pub fn sample_uniform(&mut self, rng: Range<f64>) -> f64 {
            let p = self.next_f64();
            rng.start * p + rng.end * (1.0 - p)
        }

        /// Returns true of probability p
        pub fn next_bool(&mut self, p_true: f64) -> bool {
            assert!((0.0..=1.0).contains(&p_true));
            self.next_f64() < p_true
        }
    }
}

fn main() {
    let [n, pa, pb] = read_tuple::<String, 3>();
    let n: usize = n.parse().unwrap();
    let (pa, pb): (f64, f64) = (pa.parse().unwrap(), pb.parse().unwrap());

    let mut a = read_vec::<u16>();
    let mut b = read_vec::<u16>();
    a.sort_unstable_by_key(|x| Reverse(*x));
    b.sort_unstable_by_key(|x| Reverse(*x));
    let mut rng = rng::SplitMix64::new(0x1723);

    let find_idx = |n: usize, p: f64, v: f64| -> usize {
        if v < p {
            n - 1
        } else {
            let v = (v - p) / (1.0 - p);
            (v * (n - 1) as f64) as usize
        }
    };

    const ROUND: i32 = 2_000_000;
    let mut ans = 0;
    let cutoff = a.iter().sum::<u16>() + b.iter().sum::<u16>();
    for _ in 0..ROUND {
        let mut a = a.clone();
        let mut b = b.clone();
        let mut a_win = 0;
        let mut b_win = 0;
        for i in 0..n {
            let sa = find_idx(n - i, pa, rng.next_f64());

            let sb = find_idx(n - i, pb, rng.next_f64());
            if a[sa] > b[sb] {
                a_win += a[sa] + b[sb];
            } else {
                b_win += a[sa] + b[sb];
            }
            a.remove(sa);
            b.remove(sb);
            if a_win >= cutoff / 2 + 1 || b_win >= (cutoff + 1) / 2 {
                break;
            }
        }
        if a_win > b_win {
            ans += 1
        }
    }
    println!("{:.7}", ans as f64 / ROUND as f64);
}

mod fio {
    use std::{
        cell::RefCell,
        convert::TryInto,
        fmt::Debug,
        io::{BufRead, BufWriter, StdinLock, StdoutLock, stdin, stdout},
        str::FromStr,
    };
    thread_local! {
        pub static STDIN: RefCell<StdinLock<'static>> = RefCell::new(stdin().lock());
        pub static STDOUT: RefCell<BufWriter<StdoutLock<'static>>> = RefCell::new(BufWriter::new(stdout().lock()));
    }

    #[allow(dead_code)]
    pub fn read<T: FromStr>() -> T
    where
        <T as FromStr>::Err: Debug,
    {
        read_line().parse().unwrap()
    }

    #[allow(dead_code)]
    pub fn read_vec<T: FromStr>() -> Vec<T>
    where
        <T as FromStr>::Err: Debug,
    {
        read_line()
            .split_whitespace()
            .map(|x| x.parse().unwrap())
            .collect()
    }

    #[allow(dead_code)]
    pub fn read_tuple<T, const N: usize>() -> [T; N]
    where
        T: FromStr + Debug,
        <T as FromStr>::Err: Debug,
    {
        read_vec::<T>().try_into().unwrap()
    }

    /// whitespace at the end of the line is ignored
    pub fn read_line() -> String {
        let mut s = String::new();
        STDIN.with(|cell| {
            cell.borrow_mut().read_line(&mut s).unwrap();
        });
        String::from_str(s.trim_end()).unwrap()
    }
}

#[macro_export]
macro_rules! print {
    ($($t:tt)*) => {
        fio::STDOUT.with(|cell|{
            use std::io::Write;
            write!(cell.borrow_mut(), $($t)*).unwrap()
        })};
}

#[macro_export]
macro_rules! println {
    ($($t:tt)*) => {
        fio::STDOUT.with(|cell| {
            use std::io::Write;
            writeln!(cell.borrow_mut(), $($t)*).unwrap()
        })
    };
}

#[macro_export]
macro_rules! flush {
    () => {
        fio::STDOUT.with(|cell| {
            use std::io::Write;
            cell.borrow_mut().flush().unwrap()
        });
    };
}
0