Submission #71726869


Source Code Expand

/*
[Dependencies]
proconio = "0.4.5"
*/

use proconio::input;
use std::cmp::Ordering;
use std::time::Instant;

// =====================
// Hyperparameters
// =====================
const TIME_LIMIT_SEC: f64 = 1.95;

// Beam
const BEAM_WIDTH: usize = 15;
const HORIZON: usize = 10;
const BRANCH: usize = 40;

// SA
const SA_T0: f64 = 1e-1;
const SA_T1: f64 = 1e-5;
const SA_SAFE_MARGIN: f64 = 0.02;
const SA_FORCE_ACCEPT_EPS: f64 = 1e-12;

// 近傍操作の選択確率(合計 1.0 を想定)
const P_MOVE_POINT: f64 = 0.55; // 1点変更
const P_MOVE_SWAP: f64 = 0.25; // swap
const P_MOVE_REWRITE: f64 = 0.20; // 区間リライト
const REWRITE_MAX_LEN: usize = 16;

const RNG_SEED: u64 = 123456789;

// --- logging ---
const LOG: bool = false;

// =====================
// Problem fixed constants
// =====================
const N: usize = 10;
const L: usize = 4;

// ========== RNG ==========
#[derive(Clone)]
struct XorShift64 {
    s: u64,
}
impl XorShift64 {
    fn new(seed: u64) -> Self {
        let s = if seed == 0 { 88172645463325252 } else { seed };
        Self { s }
    }
    #[inline(always)]
    fn next_u64(&mut self) -> u64 {
        let mut x = self.s;
        x ^= x << 7;
        x ^= x >> 9;
        x ^= x << 8;
        self.s = x;
        x
    }
    #[inline(always)]
    fn next_f64(&mut self) -> f64 {
        const DEN: f64 = (u64::MAX as f64) + 1.0;
        (self.next_u64() as f64) / DEN
    }
    #[inline(always)]
    fn gen_usize(&mut self, upper: usize) -> usize {
        (self.next_u64() % (upper as u64)) as usize
    }
}

// ========== Data Structures ==========
#[derive(Clone)]
struct ProblemInput {
    t: usize,
    k0: u128,
    a: [u128; N],
    c: [[u128; N]; L],
    cost_scale: [f64; N],
}

#[derive(Clone)]
struct State {
    apples: u128,
    b: [[u128; N]; L],
    p: [[u32; N]; L],
}

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
enum Action {
    Upgrade { level: usize, id: usize },
    Wait,
}
impl Action {
    fn format(&self) -> String {
        match *self {
            Action::Upgrade { level, id } => format!("{level} {id}"),
            Action::Wait => "-1".to_string(),
        }
    }
}

#[derive(Clone)]
struct Node {
    st: State,
    first: Action,
    score: f64,
    is_wait_line: bool,
}

// ========== Optimization Helpers ==========
#[derive(Clone, Copy)]
struct Coeffs {
    c1: u128,
    c2: u128,
    c3: u128,
    c4: u128,
}

#[inline(always)]
fn calc_coeffs(rem: usize) -> Coeffs {
    let r = rem as u128;
    if r == 0 {
        return Coeffs {
            c1: 0,
            c2: 0,
            c3: 0,
            c4: 0,
        };
    }
    let c1 = r;
    let c2 = r.saturating_mul(r.saturating_sub(1)) / 2;
    let c3 = r
        .saturating_mul(r.saturating_sub(1))
        .saturating_mul(r.saturating_sub(2))
        / 6;
    let c4 = r
        .saturating_mul(r.saturating_sub(1))
        .saturating_mul(r.saturating_sub(2))
        .saturating_mul(r.saturating_sub(3))
        / 24;
    Coeffs { c1, c2, c3, c4 }
}

#[inline(always)]
fn prereq_ok(st: &State, level: usize, j: usize) -> bool {
    unsafe {
        match level {
            0 => true,
            1 => *st.p.get_unchecked(0).get_unchecked(j) > 0,
            2 => {
                *st.p.get_unchecked(0).get_unchecked(j) > 0
                    && *st.p.get_unchecked(1).get_unchecked(j) > 0
            }
            3 => {
                *st.p.get_unchecked(0).get_unchecked(j) > 0
                    && *st.p.get_unchecked(1).get_unchecked(j) > 0
                    && *st.p.get_unchecked(2).get_unchecked(j) > 0
            }
            _ => false,
        }
    }
}

#[inline(always)]
fn upgrade_cost(inp: &ProblemInput, st: &State, level: usize, j: usize) -> u128 {
    unsafe {
        inp.c
            .get_unchecked(level)
            .get_unchecked(j)
            .saturating_mul((*st.p.get_unchecked(level).get_unchecked(j) as u128).saturating_add(1))
    }
}

#[inline(always)]
fn estimate_end_apples_fast(inp: &ProblemInput, st: &State, coeffs: &Coeffs) -> u128 {
    let mut ans = st.apples;
    unsafe {
        for j in 0..N {
            let a = *inp.a.get_unchecked(j);
            let p0 = *st.p.get_unchecked(0).get_unchecked(j) as u128;
            if p0 == 0 {
                continue;
            }
            let b0 = *st.b.get_unchecked(0).get_unchecked(j);

            ans = ans.saturating_add(
                a.saturating_mul(b0)
                    .saturating_mul(p0)
                    .saturating_mul(coeffs.c1),
            );

            let p1 = *st.p.get_unchecked(1).get_unchecked(j) as u128;
            if p1 == 0 {
                continue;
            }
            ans = ans.saturating_add(
                a.saturating_mul(*st.b.get_unchecked(1).get_unchecked(j))
                    .saturating_mul(p0)
                    .saturating_mul(p1)
                    .saturating_mul(coeffs.c2),
            );

            let p2 = *st.p.get_unchecked(2).get_unchecked(j) as u128;
            if p2 == 0 {
                continue;
            }
            ans = ans.saturating_add(
                a.saturating_mul(*st.b.get_unchecked(2).get_unchecked(j))
                    .saturating_mul(p0)
                    .saturating_mul(p1)
                    .saturating_mul(p2)
                    .saturating_mul(coeffs.c3),
            );

            let p3 = *st.p.get_unchecked(3).get_unchecked(j) as u128;
            if p3 == 0 {
                continue;
            }
            ans = ans.saturating_add(
                a.saturating_mul(*st.b.get_unchecked(3).get_unchecked(j))
                    .saturating_mul(p0)
                    .saturating_mul(p1)
                    .saturating_mul(p2)
                    .saturating_mul(p3)
                    .saturating_mul(coeffs.c4),
            );
        }
    }
    ans
}

fn score_state_fast(inp: &ProblemInput, st: &State, rem: usize, coeffs: &Coeffs) -> f64 {
    let r_f64 = rem.max(1) as f64;
    let end = estimate_end_apples_fast(inp, st, coeffs);
    let growth = end.saturating_sub(st.apples);
    let avg = (growth as f64) / r_f64;
    (avg + 1.0).ln()
}

#[inline(always)]
fn step_unchecked(inp: &ProblemInput, st: &mut State, act: Action) {
    unsafe {
        if let Action::Upgrade { level, id } = act {
            let cost = upgrade_cost(inp, st, level, id);
            st.apples = st.apples.saturating_sub(cost);
            *st.p.get_unchecked_mut(level).get_unchecked_mut(id) += 1;
        }

        for j in 0..N {
            let inc = inp
                .a
                .get_unchecked(j)
                .saturating_mul(*st.b.get_unchecked(0).get_unchecked(j))
                .saturating_mul(*st.p.get_unchecked(0).get_unchecked(j) as u128);
            st.apples = st.apples.saturating_add(inc);
        }

        // i=1 -> b0
        for j in 0..N {
            let inc =
                st.b.get_unchecked(1)
                    .get_unchecked(j)
                    .saturating_mul(*st.p.get_unchecked(1).get_unchecked(j) as u128);
            let b0 = st.b.get_unchecked_mut(0).get_unchecked_mut(j);
            *b0 = b0.saturating_add(inc);
        }
        // i=2 -> b1
        for j in 0..N {
            let inc =
                st.b.get_unchecked(2)
                    .get_unchecked(j)
                    .saturating_mul(*st.p.get_unchecked(2).get_unchecked(j) as u128);
            let b1 = st.b.get_unchecked_mut(1).get_unchecked_mut(j);
            *b1 = b1.saturating_add(inc);
        }
        // i=3 -> b2
        for j in 0..N {
            let inc =
                st.b.get_unchecked(3)
                    .get_unchecked(j)
                    .saturating_mul(*st.p.get_unchecked(3).get_unchecked(j) as u128);
            let b2 = st.b.get_unchecked_mut(2).get_unchecked_mut(j);
            *b2 = b2.saturating_add(inc);
        }
    }
}

#[inline(always)]
fn apply_action_checked(inp: &ProblemInput, st: &mut State, act: Action) -> bool {
    if let Action::Upgrade { level, id } = act {
        let cost = upgrade_cost(inp, st, level, id);
        if cost > st.apples {
            return false;
        }
    }
    step_unchecked(inp, st, act);
    true
}

// ========== Candidate Buf ==========
struct CandidateBuf {
    data: [(f64, Action); BRANCH],
    len: usize,
}
impl CandidateBuf {
    fn new() -> Self {
        Self {
            data: [(0.0, Action::Wait); BRANCH],
            len: 0,
        }
    }
    fn reset(&mut self) {
        self.len = 0;
    }
    fn push(&mut self, score: f64, act: Action) {
        if self.len == BRANCH && score <= self.data[BRANCH - 1].0 {
            return;
        }
        let mut pos = self.len;
        while pos > 0 && self.data[pos - 1].0 < score {
            pos -= 1;
        }
        if pos < BRANCH {
            if self.len < BRANCH {
                self.len += 1;
            }
            unsafe {
                let ptr = self.data.as_mut_ptr();
                for i in (pos + 1..self.len).rev() {
                    *ptr.add(i) = *ptr.add(i - 1);
                }
                *ptr.add(pos) = (score, act);
            }
        }
    }
}

fn gen_top_candidates_fast(
    inp: &ProblemInput,
    st: &State,
    rem_turns: usize,
    coeffs: &Coeffs,
    out: &mut CandidateBuf,
) {
    out.reset();
    let rem_f = rem_turns.max(20) as f64;

    for i in 0..L {
        for j in 0..N {
            if !prereq_ok(st, i, j) {
                continue;
            }
            let cost = upgrade_cost(inp, st, i, j);
            if cost == 0 || cost > st.apples {
                continue;
            }

            let a = inp.a[j];
            unsafe {
                let p0 = *st.p.get_unchecked(0).get_unchecked(j) as u128;
                let b0 = *st.b.get_unchecked(0).get_unchecked(j);

                let delta = match i {
                    0 => {
                        let p1 = *st.p.get_unchecked(1).get_unchecked(j) as u128;
                        let b1 = *st.b.get_unchecked(1).get_unchecked(j);
                        let t1 = a.saturating_mul(b0).saturating_mul(coeffs.c1);
                        if p1 == 0 {
                            t1
                        } else {
                            let t2 = a
                                .saturating_mul(b1)
                                .saturating_mul(p1)
                                .saturating_mul(coeffs.c2);
                            let p2 = *st.p.get_unchecked(2).get_unchecked(j) as u128;
                            if p2 == 0 {
                                t1.saturating_add(t2)
                            } else {
                                let b2 = *st.b.get_unchecked(2).get_unchecked(j);
                                let t3 = a
                                    .saturating_mul(b2)
                                    .saturating_mul(p1)
                                    .saturating_mul(p2)
                                    .saturating_mul(coeffs.c3);
                                let p3 = *st.p.get_unchecked(3).get_unchecked(j) as u128;
                                if p3 == 0 {
                                    t1.saturating_add(t2).saturating_add(t3)
                                } else {
                                    let b3 = *st.b.get_unchecked(3).get_unchecked(j);
                                    let t4 = a
                                        .saturating_mul(b3)
                                        .saturating_mul(p1)
                                        .saturating_mul(p2)
                                        .saturating_mul(p3)
                                        .saturating_mul(coeffs.c4);
                                    t1.saturating_add(t2).saturating_add(t3).saturating_add(t4)
                                }
                            }
                        }
                    }
                    1 => {
                        if p0 == 0 {
                            0
                        } else {
                            let b1 = *st.b.get_unchecked(1).get_unchecked(j);
                            let t2 = a
                                .saturating_mul(p0)
                                .saturating_mul(b1)
                                .saturating_mul(coeffs.c2);
                            let p2 = *st.p.get_unchecked(2).get_unchecked(j) as u128;
                            if p2 == 0 {
                                t2
                            } else {
                                let b2 = *st.b.get_unchecked(2).get_unchecked(j);
                                let t3 = a
                                    .saturating_mul(p0)
                                    .saturating_mul(b2)
                                    .saturating_mul(p2)
                                    .saturating_mul(coeffs.c3);
                                let p3 = *st.p.get_unchecked(3).get_unchecked(j) as u128;
                                if p3 == 0 {
                                    t2.saturating_add(t3)
                                } else {
                                    let b3 = *st.b.get_unchecked(3).get_unchecked(j);
                                    let t4 = a
                                        .saturating_mul(p0)
                                        .saturating_mul(b3)
                                        .saturating_mul(p2)
                                        .saturating_mul(p3)
                                        .saturating_mul(coeffs.c4);
                                    t2.saturating_add(t3).saturating_add(t4)
                                }
                            }
                        }
                    }
                    2 => {
                        if p0 == 0 {
                            0
                        } else {
                            let p1 = *st.p.get_unchecked(1).get_unchecked(j) as u128;
                            if p1 == 0 {
                                0
                            } else {
                                let b2 = *st.b.get_unchecked(2).get_unchecked(j);
                                let t3 = a
                                    .saturating_mul(p0)
                                    .saturating_mul(p1)
                                    .saturating_mul(b2)
                                    .saturating_mul(coeffs.c3);
                                let p3 = *st.p.get_unchecked(3).get_unchecked(j) as u128;
                                if p3 == 0 {
                                    t3
                                } else {
                                    let b3 = *st.b.get_unchecked(3).get_unchecked(j);
                                    let t4 = a
                                        .saturating_mul(p0)
                                        .saturating_mul(p1)
                                        .saturating_mul(b3)
                                        .saturating_mul(p3)
                                        .saturating_mul(coeffs.c4);
                                    t3.saturating_add(t4)
                                }
                            }
                        }
                    }
                    3 => {
                        let p1 = *st.p.get_unchecked(1).get_unchecked(j) as u128;
                        let p2 = *st.p.get_unchecked(2).get_unchecked(j) as u128;
                        if p0 == 0 || p1 == 0 || p2 == 0 {
                            0
                        } else {
                            let b3 = *st.b.get_unchecked(3).get_unchecked(j);
                            a.saturating_mul(p0)
                                .saturating_mul(p1)
                                .saturating_mul(p2)
                                .saturating_mul(b3)
                                .saturating_mul(coeffs.c4)
                        }
                    }
                    _ => 0,
                };
                if delta == 0 {
                    continue;
                }

                let dynamic_weight = if i == 0 {
                    1.0
                } else {
                    (inp.cost_scale[j] / rem_f).powi(i as i32)
                };
                let eff = (delta as f64) / (cost as f64) * dynamic_weight;
                out.push(eff, Action::Upgrade { level: i, id: j });
            }
        }
    }
}

// ========== Rolling Horizon Beam Search ==========
fn plan_next_action(
    inp: &ProblemInput,
    cur: &State,
    turn: usize,
    rng: &mut XorShift64,
    beam_buf: &mut Vec<Node>,
    next_buf: &mut Vec<Node>,
    cand_buf: &mut CandidateBuf,
) -> Action {
    let remaining = inp.t.saturating_sub(turn);
    if remaining == 0 {
        return Action::Wait;
    }
    let horizon = HORIZON.min(remaining);

    beam_buf.clear();
    let coeffs_now = calc_coeffs(remaining);
    beam_buf.push(Node {
        st: cur.clone(),
        first: Action::Wait,
        score: score_state_fast(inp, cur, remaining, &coeffs_now),
        is_wait_line: true,
    });

    for depth in 0..horizon {
        let rem_time = remaining - depth;
        let next_rem = rem_time.saturating_sub(1);

        let coeffs_curr = calc_coeffs(rem_time);
        let coeffs_next = calc_coeffs(next_rem);

        next_buf.clear();

        for node in beam_buf.iter() {
            gen_top_candidates_fast(inp, &node.st, rem_time, &coeffs_curr, cand_buf);

            // Wait
            {
                let mut st2 = node.st.clone();
                let _ = apply_action_checked(inp, &mut st2, Action::Wait);
                let first = if depth == 0 { Action::Wait } else { node.first };
                let sc =
                    score_state_fast(inp, &st2, next_rem, &coeffs_next) + rng.next_f64() * 1e-9;

                next_buf.push(Node {
                    st: st2,
                    first,
                    score: sc,
                    is_wait_line: node.is_wait_line,
                });
            }

            // Upgrades
            for i in 0..cand_buf.len {
                let act = cand_buf.data[i].1;
                let mut st2 = node.st.clone();
                if !apply_action_checked(inp, &mut st2, act) {
                    continue;
                }
                let first = if depth == 0 { act } else { node.first };
                let sc =
                    score_state_fast(inp, &st2, next_rem, &coeffs_next) + rng.next_f64() * 1e-9;

                next_buf.push(Node {
                    st: st2,
                    first,
                    score: sc,
                    is_wait_line: false,
                });
            }
        }

        next_buf.sort_unstable_by(|a, b| b.score.partial_cmp(&a.score).unwrap_or(Ordering::Equal));

        let has_wait_line_in_top = next_buf.iter().take(BEAM_WIDTH).any(|n| n.is_wait_line);
        if next_buf.len() > BEAM_WIDTH {
            if !has_wait_line_in_top {
                if let Some(pos) = next_buf.iter().position(|n| n.is_wait_line) {
                    next_buf.swap(BEAM_WIDTH - 1, pos);
                }
            }
            next_buf.truncate(BEAM_WIDTH);
        }

        std::mem::swap(beam_buf, next_buf);
        if beam_buf.is_empty() {
            break;
        }
    }

    beam_buf.first().map(|n| n.first).unwrap_or(Action::Wait)
}

fn greedy_fallback(
    inp: &ProblemInput,
    st: &State,
    turn: usize,
    cand_buf: &mut CandidateBuf,
) -> Action {
    let rem = inp.t.saturating_sub(turn);
    let coeffs = calc_coeffs(rem);
    gen_top_candidates_fast(inp, st, rem, &coeffs, cand_buf);
    if cand_buf.len > 0 {
        cand_buf.data[0].1
    } else {
        Action::Wait
    }
}

// ========== SA helpers ==========
#[inline(always)]
fn score_final(apples: u128) -> f64 {
    ((apples.max(1)) as f64).ln()
}

fn simulate_full_inplace(
    inp: &ProblemInput,
    actions: &[Action],
    state_before: &mut Vec<State>,
) -> (bool, u128) {
    state_before.clear();
    let mut st = State {
        apples: inp.k0,
        b: [[1u128; N]; L],
        p: [[0u32; N]; L],
    };
    state_before.push(st.clone());
    for t in 0..inp.t {
        if !apply_action_checked(inp, &mut st, actions[t]) {
            return (false, 0);
        }
        state_before.push(st.clone());
    }
    (true, st.apples)
}

#[inline(always)]
fn temperature(elapsed: f64, deadline: f64) -> f64 {
    let x = (elapsed / deadline).min(1.0);
    SA_T0 * (SA_T1 / SA_T0).powf(x)
}

#[inline(always)]
fn sample_affordable_action(
    rng: &mut XorShift64,
    inp: &ProblemInput,
    st_before: &State,
    current: Action,
) -> Action {
    let mut opts: [Action; 41] = [Action::Wait; 41];
    let mut m: usize = 0;

    unsafe {
        for level in 0..L {
            for id in 0..N {
                let cost = inp.c.get_unchecked(level).get_unchecked(id).saturating_mul(
                    (*st_before.p.get_unchecked(level).get_unchecked(id) as u128).saturating_add(1),
                );
                if cost <= st_before.apples {
                    opts[m] = Action::Upgrade { level, id };
                    m += 1;
                }
            }
        }
    }
    opts[m] = Action::Wait;
    let total = m + 1;

    if total == 1 {
        return Action::Wait;
    }

    loop {
        let cand = opts[rng.gen_usize(total)];
        if cand != current {
            return cand;
        }
        if total < 5 {
            // fallback
            let mut candidates: [Action; 41] = [Action::Wait; 41];
            let mut k: usize = 0;
            for i in 0..total {
                if opts[i] != current {
                    candidates[k] = opts[i];
                    k += 1;
                }
            }
            if k == 0 {
                return current;
            }
            return candidates[rng.gen_usize(k)];
        }
    }
}

// 出力前の安全弁:払えない upgrade を wait に落として必ず合法化
fn repair_to_valid(inp: &ProblemInput, actions: &mut [Action]) -> u128 {
    let mut st = State {
        apples: inp.k0,
        b: [[1u128; N]; L],
        p: [[0u32; N]; L],
    };
    for t in 0..inp.t {
        if !apply_action_checked(inp, &mut st, actions[t]) {
            actions[t] = Action::Wait;
            let _ = apply_action_checked(inp, &mut st, Action::Wait);
        }
    }
    st.apples
}

// ========== Main ==========
fn main() {
    input! {
        _n_in: usize, _l_in: usize,
        t: usize, k: u64,
        a_in: [u64; N],
        c_in: [[u64; N]; L],
    }

    let mut a = [0u128; N];
    for j in 0..N {
        a[j] = a_in[j] as u128;
    }
    let mut c = [[0u128; N]; L];
    for i in 0..L {
        for j in 0..N {
            c[i][j] = c_in[i][j] as u128;
        }
    }

    let mut cost_scale = [0.0; N];
    for j in 0..N {
        let c0 = c[0][j].max(1) as f64;
        let c3 = c[3][j].max(1) as f64;
        cost_scale[j] = (c3 / c0).powf(1.0 / 3.0);
    }

    let inp = ProblemInput {
        t,
        k0: k as u128,
        a,
        c,
        cost_scale,
    };

    let start = Instant::now();
    let deadline = TIME_LIMIT_SEC - SA_SAFE_MARGIN;

    let mut rng = XorShift64::new(RNG_SEED);

    // ===== 1) 初期解(元ビーム) =====
    let mut beam_buf: Vec<Node> = Vec::with_capacity(BEAM_WIDTH * (BRANCH + 1));
    let mut next_buf: Vec<Node> = Vec::with_capacity(BEAM_WIDTH * (BRANCH + 1));
    let mut cand_buf = CandidateBuf::new();

    let mut actions: Vec<Action> = vec![Action::Wait; inp.t];
    let mut state_before: Vec<State> = Vec::with_capacity(inp.t + 1);

    let mut current_st = State {
        apples: inp.k0,
        b: [[1u128; N]; L],
        p: [[0u32; N]; L],
    };

    for turn in 0..inp.t {
        let elapsed = start.elapsed().as_secs_f64();
        let mode = if elapsed > deadline {
            2
        } else if elapsed > deadline - 0.10 {
            1
        } else {
            0
        };

        let act = match mode {
            2 => Action::Wait,
            1 => greedy_fallback(&inp, &current_st, turn, &mut cand_buf),
            _ => plan_next_action(
                &inp,
                &current_st,
                turn,
                &mut rng,
                &mut beam_buf,
                &mut next_buf,
                &mut cand_buf,
            ),
        };

        let mut next_st = current_st.clone();
        if apply_action_checked(&inp, &mut next_st, act) {
            actions[turn] = act;
            current_st = next_st;
        } else {
            actions[turn] = Action::Wait;
            step_unchecked(&inp, &mut current_st, Action::Wait);
        }
    }

    // 初期解を state_before に落とす
    let (ok_init, final_apples_init) = simulate_full_inplace(&inp, &actions, &mut state_before);
    if !ok_init {
        actions.fill(Action::Wait);
        let _ = simulate_full_inplace(&inp, &actions, &mut state_before);
    }

    let mut cur_actions = actions.clone();
    let mut cur_score = score_final(final_apples_init);
    let mut best_actions = cur_actions.clone();
    let mut best_score = cur_score;

    // ===== 2) SA =====
    let mut iters: u64 = 0;
    let mut accepted: u64 = 0;
    let mut improved: u64 = 0;

    // 区間リライト用バッファ(スタックに置くとでかいのでここで確保)
    let mut new_seg: Vec<Action> = Vec::with_capacity(REWRITE_MAX_LEN);

    while start.elapsed().as_secs_f64() < deadline {
        iters += 1;

        let elapsed = start.elapsed().as_secs_f64();
        let temp = temperature(elapsed, deadline);

        let u = rng.next_f64();

        if u < P_MOVE_POINT {
            // ========== 近傍1: 1点変更 ==========
            let t0 = rng.gen_usize(inp.t);
            let old_act = cur_actions[t0];
            let st_start = unsafe { state_before.get_unchecked(t0) };

            let new_act = sample_affordable_action(&mut rng, &inp, st_start, old_act);
            if new_act == old_act {
                continue;
            }

            // Dry run
            let mut dry_st = st_start.clone();
            if !apply_action_checked(&inp, &mut dry_st, new_act) {
                continue;
            }
            let mut valid = true;
            for t in (t0 + 1)..inp.t {
                let act = unsafe { *cur_actions.get_unchecked(t) };
                if !apply_action_checked(&inp, &mut dry_st, act) {
                    valid = false;
                    break;
                }
            }
            if !valid {
                continue;
            }

            let new_score = score_final(dry_st.apples);
            let delta = new_score - cur_score;
            let accept = if delta >= 0.0 {
                true
            } else {
                let p = (delta / temp.max(SA_FORCE_ACCEPT_EPS)).exp();
                rng.next_f64() < p
            };

            if accept {
                accepted += 1;
                cur_score = new_score;
                cur_actions[t0] = new_act;

                // Commit: recompute state_before from t0
                let mut commit_st = st_start.clone();
                step_unchecked(&inp, &mut commit_st, new_act);
                unsafe {
                    *state_before.get_unchecked_mut(t0 + 1) = commit_st.clone();
                    for t in (t0 + 1)..inp.t {
                        let act = *cur_actions.get_unchecked(t);
                        // dry run で合法保証されてるので unchecked
                        step_unchecked(&inp, &mut commit_st, act);
                        *state_before.get_unchecked_mut(t + 1) = commit_st.clone();
                    }
                }

                if cur_score > best_score {
                    best_score = cur_score;
                    best_actions = cur_actions.clone();
                    improved += 1;
                }
            }
        } else if u < P_MOVE_POINT + P_MOVE_SWAP {
            // ========== 近傍2: swap ==========
            let t1 = rng.gen_usize(inp.t);
            let mut t2 = rng.gen_usize(inp.t);
            while t2 == t1 {
                t2 = rng.gen_usize(inp.t);
            }
            let (l, r) = if t1 < t2 { (t1, t2) } else { (t2, t1) };

            let a_l = unsafe { *cur_actions.get_unchecked(l) };
            let a_r = unsafe { *cur_actions.get_unchecked(r) };
            if a_l == a_r {
                continue;
            }

            let st_start = unsafe { state_before.get_unchecked(l) };

            // Dry run with swapped actions
            let mut dry_st = st_start.clone();
            let mut valid = true;
            for t in l..inp.t {
                let act = if t == l {
                    a_r
                } else if t == r {
                    a_l
                } else {
                    unsafe { *cur_actions.get_unchecked(t) }
                };
                if !apply_action_checked(&inp, &mut dry_st, act) {
                    valid = false;
                    break;
                }
            }
            if !valid {
                continue;
            }

            let new_score = score_final(dry_st.apples);
            let delta = new_score - cur_score;
            let accept = if delta >= 0.0 {
                true
            } else {
                let p = (delta / temp.max(SA_FORCE_ACCEPT_EPS)).exp();
                rng.next_f64() < p
            };

            if accept {
                accepted += 1;
                cur_score = new_score;

                // Commit swap in cur_actions
                cur_actions[l] = a_r;
                cur_actions[r] = a_l;

                // Commit states from l
                let mut commit_st = st_start.clone();
                for t in l..inp.t {
                    let act = unsafe { *cur_actions.get_unchecked(t) };
                    step_unchecked(&inp, &mut commit_st, act);
                    unsafe {
                        *state_before.get_unchecked_mut(t + 1) = commit_st.clone();
                    }
                }

                if cur_score > best_score {
                    best_score = cur_score;
                    best_actions = cur_actions.clone();
                    improved += 1;
                }
            }
        } else {
            // ========== 近傍3: 区間リライト ==========
            let l = rng.gen_usize(inp.t);
            let max_len = REWRITE_MAX_LEN.min(inp.t - l);
            let len = 1 + rng.gen_usize(max_len);
            let r = l + len - 1;

            let st_start = unsafe { state_before.get_unchecked(l) };

            // segment を生成(その時点で払える行動からサンプルし、区間内で順に状態更新)
            new_seg.clear();
            let mut seg_st = st_start.clone();
            for t in l..=r {
                let old_act = unsafe { *cur_actions.get_unchecked(t) };
                let new_act = sample_affordable_action(&mut rng, &inp, &seg_st, old_act);
                // ここで払えないのが出たら候補生成ロジックがおかしいので弾く
                if !apply_action_checked(&inp, &mut seg_st, new_act) {
                    new_seg.clear();
                    break;
                }
                new_seg.push(new_act);
            }
            if new_seg.len() != len {
                continue;
            }

            // Dry run from l with rewritten segment
            let mut dry_st = st_start.clone();
            let mut valid = true;
            // apply segment
            for k in 0..len {
                let act = new_seg[k];
                if !apply_action_checked(&inp, &mut dry_st, act) {
                    valid = false;
                    break;
                }
            }
            if !valid {
                continue;
            }
            // apply tail
            for t in (r + 1)..inp.t {
                let act = unsafe { *cur_actions.get_unchecked(t) };
                if !apply_action_checked(&inp, &mut dry_st, act) {
                    valid = false;
                    break;
                }
            }
            if !valid {
                continue;
            }

            let new_score = score_final(dry_st.apples);
            let delta = new_score - cur_score;
            let accept = if delta >= 0.0 {
                true
            } else {
                let p = (delta / temp.max(SA_FORCE_ACCEPT_EPS)).exp();
                rng.next_f64() < p
            };

            if accept {
                accepted += 1;
                cur_score = new_score;

                // Commit segment into cur_actions
                for k in 0..len {
                    cur_actions[l + k] = new_seg[k];
                }

                // Commit states from l
                let mut commit_st = st_start.clone();
                for t in l..inp.t {
                    let act = unsafe { *cur_actions.get_unchecked(t) };
                    step_unchecked(&inp, &mut commit_st, act);
                    unsafe {
                        *state_before.get_unchecked_mut(t + 1) = commit_st.clone();
                    }
                }

                if cur_score > best_score {
                    best_score = cur_score;
                    best_actions = cur_actions.clone();
                    improved += 1;
                }
            }
        }
    }

    if LOG {
        eprintln!(
            "[LOG] elapsed={:.3}s iters={} accepted={} improved={} best_score={:.6}",
            start.elapsed().as_secs_f64(),
            iters,
            accepted,
            improved,
            best_score
        );
    }

    // ===== 3) 出力前に必ず合法化(WA防止)=====
    let mut out_actions = best_actions;
    let final_apples = repair_to_valid(&inp, &mut out_actions);
    if LOG {
        eprintln!(
            "[LOG] repaired_final_apples={} repaired_log_score={:.6}",
            final_apples,
            score_final(final_apples)
        );
    }

    let mut out = String::with_capacity(inp.t * 8);
    for act in out_actions {
        out.push_str(&act.format());
        out.push('\n');
    }
    print!("{out}");
}

Submission Info

Submission Time
Task A - Apple Incremental Game
User semimaru
Language Rust (rustc 1.89.0)
Score 812264945
Code Size 35314 Byte
Status AC
Exec Time 1931 ms
Memory 3872 KiB

Compile Error

warning: constant `P_MOVE_REWRITE` is never used
  --> src/main.rs:29:7
   |
29 | const P_MOVE_REWRITE: f64 = 0.20; // 区間リライト
   |       ^^^^^^^^^^^^^^
   |
   = note: `#[warn(dead_code)]` on by default

Judge Result

Set Name test_ALL
Score / Max Score 812264945 / 150000000000
Status
AC × 150
Set Name Test Cases
test_ALL test_0000.txt, test_0001.txt, test_0002.txt, test_0003.txt, test_0004.txt, test_0005.txt, test_0006.txt, test_0007.txt, test_0008.txt, test_0009.txt, test_0010.txt, test_0011.txt, test_0012.txt, test_0013.txt, test_0014.txt, test_0015.txt, test_0016.txt, test_0017.txt, test_0018.txt, test_0019.txt, test_0020.txt, test_0021.txt, test_0022.txt, test_0023.txt, test_0024.txt, test_0025.txt, test_0026.txt, test_0027.txt, test_0028.txt, test_0029.txt, test_0030.txt, test_0031.txt, test_0032.txt, test_0033.txt, test_0034.txt, test_0035.txt, test_0036.txt, test_0037.txt, test_0038.txt, test_0039.txt, test_0040.txt, test_0041.txt, test_0042.txt, test_0043.txt, test_0044.txt, test_0045.txt, test_0046.txt, test_0047.txt, test_0048.txt, test_0049.txt, test_0050.txt, test_0051.txt, test_0052.txt, test_0053.txt, test_0054.txt, test_0055.txt, test_0056.txt, test_0057.txt, test_0058.txt, test_0059.txt, test_0060.txt, test_0061.txt, test_0062.txt, test_0063.txt, test_0064.txt, test_0065.txt, test_0066.txt, test_0067.txt, test_0068.txt, test_0069.txt, test_0070.txt, test_0071.txt, test_0072.txt, test_0073.txt, test_0074.txt, test_0075.txt, test_0076.txt, test_0077.txt, test_0078.txt, test_0079.txt, test_0080.txt, test_0081.txt, test_0082.txt, test_0083.txt, test_0084.txt, test_0085.txt, test_0086.txt, test_0087.txt, test_0088.txt, test_0089.txt, test_0090.txt, test_0091.txt, test_0092.txt, test_0093.txt, test_0094.txt, test_0095.txt, test_0096.txt, test_0097.txt, test_0098.txt, test_0099.txt, test_0100.txt, test_0101.txt, test_0102.txt, test_0103.txt, test_0104.txt, test_0105.txt, test_0106.txt, test_0107.txt, test_0108.txt, test_0109.txt, test_0110.txt, test_0111.txt, test_0112.txt, test_0113.txt, test_0114.txt, test_0115.txt, test_0116.txt, test_0117.txt, test_0118.txt, test_0119.txt, test_0120.txt, test_0121.txt, test_0122.txt, test_0123.txt, test_0124.txt, test_0125.txt, test_0126.txt, test_0127.txt, test_0128.txt, test_0129.txt, test_0130.txt, test_0131.txt, test_0132.txt, test_0133.txt, test_0134.txt, test_0135.txt, test_0136.txt, test_0137.txt, test_0138.txt, test_0139.txt, test_0140.txt, test_0141.txt, test_0142.txt, test_0143.txt, test_0144.txt, test_0145.txt, test_0146.txt, test_0147.txt, test_0148.txt, test_0149.txt
Case Name Status Exec Time Memory
test_0000.txt AC 1931 ms 3672 KiB
test_0001.txt AC 1931 ms 3476 KiB
test_0002.txt AC 1931 ms 3672 KiB
test_0003.txt AC 1931 ms 3688 KiB
test_0004.txt AC 1931 ms 3584 KiB
test_0005.txt AC 1931 ms 3552 KiB
test_0006.txt AC 1931 ms 3732 KiB
test_0007.txt AC 1931 ms 3560 KiB
test_0008.txt AC 1931 ms 3528 KiB
test_0009.txt AC 1931 ms 3704 KiB
test_0010.txt AC 1931 ms 3680 KiB
test_0011.txt AC 1931 ms 3400 KiB
test_0012.txt AC 1931 ms 3684 KiB
test_0013.txt AC 1931 ms 3676 KiB
test_0014.txt AC 1931 ms 3468 KiB
test_0015.txt AC 1931 ms 3600 KiB
test_0016.txt AC 1931 ms 3860 KiB
test_0017.txt AC 1931 ms 3684 KiB
test_0018.txt AC 1931 ms 3568 KiB
test_0019.txt AC 1931 ms 3796 KiB
test_0020.txt AC 1931 ms 3668 KiB
test_0021.txt AC 1931 ms 3576 KiB
test_0022.txt AC 1931 ms 3564 KiB
test_0023.txt AC 1931 ms 3636 KiB
test_0024.txt AC 1931 ms 3564 KiB
test_0025.txt AC 1931 ms 3796 KiB
test_0026.txt AC 1931 ms 3604 KiB
test_0027.txt AC 1931 ms 3576 KiB
test_0028.txt AC 1931 ms 3688 KiB
test_0029.txt AC 1931 ms 3468 KiB
test_0030.txt AC 1931 ms 3552 KiB
test_0031.txt AC 1931 ms 3704 KiB
test_0032.txt AC 1931 ms 3432 KiB
test_0033.txt AC 1931 ms 3572 KiB
test_0034.txt AC 1931 ms 3872 KiB
test_0035.txt AC 1931 ms 3468 KiB
test_0036.txt AC 1931 ms 3564 KiB
test_0037.txt AC 1931 ms 3420 KiB
test_0038.txt AC 1931 ms 3564 KiB
test_0039.txt AC 1931 ms 3524 KiB
test_0040.txt AC 1931 ms 3688 KiB
test_0041.txt AC 1931 ms 3720 KiB
test_0042.txt AC 1931 ms 3560 KiB
test_0043.txt AC 1931 ms 3540 KiB
test_0044.txt AC 1931 ms 3552 KiB
test_0045.txt AC 1931 ms 3804 KiB
test_0046.txt AC 1931 ms 3648 KiB
test_0047.txt AC 1931 ms 3600 KiB
test_0048.txt AC 1931 ms 3680 KiB
test_0049.txt AC 1931 ms 3552 KiB
test_0050.txt AC 1931 ms 3688 KiB
test_0051.txt AC 1931 ms 3764 KiB
test_0052.txt AC 1931 ms 3596 KiB
test_0053.txt AC 1931 ms 3704 KiB
test_0054.txt AC 1931 ms 3720 KiB
test_0055.txt AC 1931 ms 3796 KiB
test_0056.txt AC 1931 ms 3704 KiB
test_0057.txt AC 1931 ms 3584 KiB
test_0058.txt AC 1931 ms 3636 KiB
test_0059.txt AC 1931 ms 3692 KiB
test_0060.txt AC 1931 ms 3556 KiB
test_0061.txt AC 1931 ms 3668 KiB
test_0062.txt AC 1931 ms 3804 KiB
test_0063.txt AC 1931 ms 3508 KiB
test_0064.txt AC 1931 ms 3584 KiB
test_0065.txt AC 1931 ms 3640 KiB
test_0066.txt AC 1931 ms 3672 KiB
test_0067.txt AC 1931 ms 3672 KiB
test_0068.txt AC 1931 ms 3688 KiB
test_0069.txt AC 1931 ms 3404 KiB
test_0070.txt AC 1931 ms 3424 KiB
test_0071.txt AC 1931 ms 3636 KiB
test_0072.txt AC 1931 ms 3852 KiB
test_0073.txt AC 1931 ms 3604 KiB
test_0074.txt AC 1931 ms 3548 KiB
test_0075.txt AC 1931 ms 3648 KiB
test_0076.txt AC 1931 ms 3732 KiB
test_0077.txt AC 1931 ms 3512 KiB
test_0078.txt AC 1931 ms 3568 KiB
test_0079.txt AC 1931 ms 3556 KiB
test_0080.txt AC 1931 ms 3744 KiB
test_0081.txt AC 1931 ms 3724 KiB
test_0082.txt AC 1931 ms 3724 KiB
test_0083.txt AC 1931 ms 3672 KiB
test_0084.txt AC 1931 ms 3492 KiB
test_0085.txt AC 1931 ms 3616 KiB
test_0086.txt AC 1931 ms 3688 KiB
test_0087.txt AC 1931 ms 3660 KiB
test_0088.txt AC 1931 ms 3468 KiB
test_0089.txt AC 1931 ms 3576 KiB
test_0090.txt AC 1931 ms 3872 KiB
test_0091.txt AC 1931 ms 3616 KiB
test_0092.txt AC 1931 ms 3588 KiB
test_0093.txt AC 1931 ms 3584 KiB
test_0094.txt AC 1931 ms 3672 KiB
test_0095.txt AC 1931 ms 3564 KiB
test_0096.txt AC 1931 ms 3656 KiB
test_0097.txt AC 1931 ms 3872 KiB
test_0098.txt AC 1931 ms 3624 KiB
test_0099.txt AC 1931 ms 3576 KiB
test_0100.txt AC 1931 ms 3544 KiB
test_0101.txt AC 1931 ms 3564 KiB
test_0102.txt AC 1931 ms 3604 KiB
test_0103.txt AC 1931 ms 3820 KiB
test_0104.txt AC 1931 ms 3576 KiB
test_0105.txt AC 1931 ms 3668 KiB
test_0106.txt AC 1931 ms 3668 KiB
test_0107.txt AC 1931 ms 3556 KiB
test_0108.txt AC 1931 ms 3632 KiB
test_0109.txt AC 1931 ms 3616 KiB
test_0110.txt AC 1931 ms 3556 KiB
test_0111.txt AC 1931 ms 3860 KiB
test_0112.txt AC 1931 ms 3608 KiB
test_0113.txt AC 1931 ms 3436 KiB
test_0114.txt AC 1931 ms 3464 KiB
test_0115.txt AC 1931 ms 3692 KiB
test_0116.txt AC 1931 ms 3392 KiB
test_0117.txt AC 1931 ms 3492 KiB
test_0118.txt AC 1931 ms 3676 KiB
test_0119.txt AC 1931 ms 3488 KiB
test_0120.txt AC 1931 ms 3580 KiB
test_0121.txt AC 1931 ms 3592 KiB
test_0122.txt AC 1931 ms 3516 KiB
test_0123.txt AC 1931 ms 3744 KiB
test_0124.txt AC 1931 ms 3448 KiB
test_0125.txt AC 1931 ms 3552 KiB
test_0126.txt AC 1931 ms 3648 KiB
test_0127.txt AC 1931 ms 3552 KiB
test_0128.txt AC 1931 ms 3620 KiB
test_0129.txt AC 1931 ms 3712 KiB
test_0130.txt AC 1931 ms 3632 KiB
test_0131.txt AC 1931 ms 3680 KiB
test_0132.txt AC 1931 ms 3712 KiB
test_0133.txt AC 1931 ms 3596 KiB
test_0134.txt AC 1931 ms 3796 KiB
test_0135.txt AC 1931 ms 3688 KiB
test_0136.txt AC 1931 ms 3508 KiB
test_0137.txt AC 1931 ms 3552 KiB
test_0138.txt AC 1931 ms 3712 KiB
test_0139.txt AC 1931 ms 3484 KiB
test_0140.txt AC 1931 ms 3476 KiB
test_0141.txt AC 1931 ms 3436 KiB
test_0142.txt AC 1931 ms 3688 KiB
test_0143.txt AC 1931 ms 3444 KiB
test_0144.txt AC 1931 ms 3668 KiB
test_0145.txt AC 1931 ms 3584 KiB
test_0146.txt AC 1931 ms 3644 KiB
test_0147.txt AC 1931 ms 3576 KiB
test_0148.txt AC 1931 ms 3724 KiB
test_0149.txt AC 1931 ms 3560 KiB