提出 #58799365


ソースコード 拡げる

#![allow(non_snake_case)]

use fixedbitset::FixedBitSet;
use proconio::{input, marker::Chars};
use rustc_hash::FxHashSet;
use std::collections::BinaryHeap;

fn main() {
    get_time();
    let input = read_input();
    let mut ret = Solution {
        arm: Arm::new(vec![]),
        root: (0, 0),
        moves: vec![],
    };
    let width1 = (1.5e5 / (input.V as f64 * input.M as f64)).round().max(1.0) as usize;
    eprintln!("!log width1 {}", width1);
    let mut best = !0;
    // 小さい幅のビームサーチでアームを色々試してみて、良さげなアームを求める
    for stem in (if input.V <= 6 { 1 } else { 2 }..=3.min(input.V - 1)).rev() {
        // 長さ stem のパスの先で分岐して残りを葉とする
        let mut ls = vec![input.N as i32 * 2 / 5];
        for i in 0..stem - 1 {
            let l = (ls[i] + 1) / 2;
            ls.push(l);
        }
        if ls[stem - 1] == 1 {
            continue;
        }
        ls.reverse();
        for b in 1..[6, 3, 2][stem - 1] {
            let mut arm = vec![];
            for i in 0..stem {
                arm.push((i, ls[i]));
            }
            for i in 0..input.V - 1 - stem {
                arm.push((stem, b + i as i32));
            }
            let arm = Arm::new(arm);
            if let Some(sol) = solve_beam(&input, arm, width1, false, best) {
                best = sol.moves.len();
                ret = sol;
                eprintln!("{:.3}: {} ({:?})", get_time(), best, ret.arm.pL);
            }
        }
    }
    if ret.arm.num_joint == 4 {
        // 長さ 4 のパスの頂点 3 と 4 に残りを葉としてつなげる
        let mut ls = vec![input.N as i32 * 2 / 5];
        for i in 0..2 {
            let l = (ls[i] + 1) / 2;
            ls.push(l);
        }
        ls.reverse();
        let mut arm = vec![];
        for i in 0..3 {
            arm.push((i, ls[i]));
        }
        arm.push((3, 1));
        let leaf1 = (input.V - 1 - 4) / 3;
        for i in 0..leaf1 {
            arm.push((3, 1 + i as i32));
        }
        for i in 0..(input.V - 1 - 4 - leaf1) {
            arm.push((4, 1 + i as i32));
        }
        let arm = Arm::new(arm);
        if let Some(sol) = solve_beam(&input, arm, width1, false, best) {
            best = sol.moves.len();
            ret = sol;
            eprintln!("{:.3}: {} ({:?})", get_time(), best, ret.arm.pL);
        }
    }
    eprintln!("!log time1 {:.3}", get_time());
    // 残りの時間で大きな幅でビームサーチして解を更新
    let width = (1e7 * (2.9 - get_time())
        / (f64::powi(3.0, ret.arm.num_joint as i32 - 1) * input.V as f64 * best as f64))
        .round()
        .max(1.0) as usize;
    eprintln!("!log width {width}");
    if let Some(sol) = solve_beam(&input, ret.arm.clone(), width, true, best) {
        best = sol.moves.len();
        ret = sol;
        eprintln!("{:.3}: {}", get_time(), best);
    }
    write_output(&ret);
    eprintln!("!log stem {}", ret.arm.num_joint - 1);
    eprintln!("Time = {:.3}", get_time());
}

/// 指定された腕に対し、ビームサーチで解を求める
fn solve_beam(
    input: &Input,
    arm: Arm,
    width: usize,
    auto_width: bool,
    best: usize,
) -> Option<Solution> {
    let root = (input.N as i32 / 2, input.N as i32 / 2);
    // 指先を移動させにくいマスは重みを大きくして重みの総和を評価値とする
    let weight = get_weight(input, &arm)?;
    let mut trace = Trace::new();
    let mut beam = vec![];
    // 初期位置は中心に近いマスからwidth個を選択
    let mut tmp = vec![];
    for i in 0..input.N as i32 {
        for j in 0..input.N as i32 {
            tmp.push(((i - root.0).abs() + (j - root.1).abs(), i, j));
        }
    }
    tmp.sort();
    tmp.truncate(width * 5);
    for (_, i, j) in tmp {
        let mut state = State::new(&input, &arm, (i, j), &weight);
        state.id = trace.add(vec![i as u8, j as u8], state.id);
        beam.push(state);
    }
    let mut used = FastBitSet::new(input.N2);
    let mut ps = vec![(0, 0, 0); arm.num_joint];
    let mut crt = 0;
    let stime = get_time();
    if stime >= 2.9 {
        return None;
    }
    let mut total_width = 0;
    let mut prev_width = width;
    while beam[0].eval > 0 {
        let width = if !auto_width || crt <= 1 {
            width
        } else {
            // これまでの幅の総和と経過時間から、幅1あたりの期待時間を求め、残りの時間とターン数に応じて幅を自動調整
            let t = (get_time() - stime) / (2.9 - stime);
            (total_width as f64 / t.max(1e-6) * (1.0 - t) / (best - crt) as f64)
                .round()
                .min(prev_width as f64 * 1.2)
                .max(1.0) as usize
        };
        prev_width = width;
        let mut cand = BoundedSortedList::new(width);
        let mut used2 = FxHashSet::default();
        for b in 0..beam.len() {
            if get_time() > 2.95 {
                return None;
            }
            let state = &beam[b];
            // 残りのターン全ての指先がフル稼働しても最適解を更新できないなら枝刈り
            if best != !0
                && crt as i64 + (state.eval % 1000 + arm.num_leaf as i64 - 1) / arm.num_leaf as i64
                    >= best as i64
            {
                continue;
            }
            let mut ok = false;
            if state.skip > 0 {
                // どの指先も稼働しなかった場合、あらゆる移動・回転が同じスコアになって多様性が失われるため、そのまま次状態を生成せずに1回休み状態としておく。
                // k(>0)回休むとk+1回の移動とあらゆる回転が可能になるのでまとめて処理する。
                for ri in 0..input.N as i32 {
                    for rj in 0..input.N as i32 {
                        if (ri - state.root.0).abs() + (rj - state.root.1).abs() > state.skip + 1
                            || !used2.insert((ri, rj, state.eval))
                        {
                            continue;
                        }
                        ps[0] = (ri, rj, 0);
                        ok |= all_rots(
                            input,
                            &arm,
                            &weight,
                            &mut cand,
                            b,
                            state,
                            &mut used,
                            &mut state.rot.clone(),
                            &mut ps,
                            1,
                        );
                    }
                }
            } else {
                // 根の移動4方向+移動なしに対し、全ての辺の回転を全通り試し、次状態を生成
                for rd in 0..5 {
                    if crt == 0 && rd != 4 {
                        continue;
                    }
                    let ri = state.root.0 + DIJ[rd].0;
                    let rj = state.root.1 + DIJ[rd].1;
                    if input.on(ri, rj) {
                        ps[0] = (ri, rj, 0);
                        ok |= all_rots(
                            input,
                            &arm,
                            &weight,
                            &mut cand,
                            b,
                            state,
                            &mut used,
                            &mut state.rot.clone(),
                            &mut ps,
                            1,
                        );
                    }
                }
            }
            if !ok {
                // どのように動かしても指先を稼働できなかった場合、1回休み状態にする
                cand.insert(state.eval, (b, -1, -1, vec![]));
            }
        }
        total_width += (beam.len() + cand.len()) / 2;
        let mut next = vec![];
        let mut used = FxHashSet::default();
        for (eval, (b, ri, rj, rot)) in cand.list() {
            let mut state = beam[b].clone();
            state.eval = eval;
            if ri == -1 && rj == -1 {
                // 一回休み状態
                state.skip += 1;
                state.last_moved.fill(false);
                if used.insert((state.root, vec![], state.holding.clone(), eval)) {
                    next.push(state);
                }
            } else {
                let mut ps = vec![(0, 0, 0); arm.num_joint];
                ps[0] = (ri, rj, 0);
                for u in 0..arm.num_joint - 1 {
                    let (p, L) = arm.pL[u];
                    let (mut i, mut j, mut r) = ps[p];
                    r = (r + rot[u]) & 3;
                    i += DIJ[r as usize].0 * L;
                    j += DIJ[r as usize].1 * L;
                    ps[u + 1] = (i, j, r);
                }
                for u in 0..arm.num_leaf {
                    let (p, L) = arm.pL[arm.num_joint - 1 + u];
                    let (i, j, r) = ps[p];
                    let r = (r + rot[arm.num_joint - 1 + u]) & 3;
                    let i = i + DIJ[r as usize].0 * L;
                    let j = j + DIJ[r as usize].1 * L;
                    state.last_moved[u] = false;
                    if input.on(i, j) {
                        let ij = i as usize * input.N + j as usize;
                        if state.holding[u] {
                            if !state.s[ij] && input.t[ij] {
                                state.s.set(ij, true);
                                state.holding[u] = false;
                                state.last_moved[u] = true;
                            }
                        } else {
                            if state.s[ij] && !input.t[ij] {
                                state.s.set(ij, false);
                                state.holding[u] = true;
                                state.last_moved[u] = true;
                            }
                        }
                    }
                }
                let mut rot2 = rot.clone();
                for u in 0..arm.num_leaf {
                    if !state.last_moved[u] {
                        rot2[arm.num_joint - 1 + u] = 4;
                    }
                }
                if used.insert(((ri, rj), rot2, state.holding.clone(), eval)) {
                    // コマンドを生成(1+休んだターン数一気に動く)
                    let mut cmds = vec![vec![b'.'; arm.V * 2]; state.skip as usize + 1];
                    let mut t = 0;
                    while state.root.0 < ri {
                        state.root.0 += 1;
                        cmds[t][0] = b'D';
                        t += 1;
                    }
                    while state.root.0 > ri {
                        state.root.0 -= 1;
                        cmds[t][0] = b'U';
                        t += 1;
                    }
                    while state.root.1 < rj {
                        state.root.1 += 1;
                        cmds[t][0] = b'R';
                        t += 1;
                    }
                    while state.root.1 > rj {
                        state.root.1 -= 1;
                        cmds[t][0] = b'L';
                        t += 1;
                    }
                    state.skip = 0;
                    state.rot = rot;
                    let cmd = cmds.last_mut().unwrap();
                    for u in 0..arm.V - 1 {
                        match (state.rot[u] + 4 - beam[b].rot[u]) & 3 {
                            1 => cmd[1 + u] = b'R',
                            2 => cmd[1 + u] = b'U', // 休み明けは180度回転可能なので`U`とし、出力時に`RR`に変換
                            3 => cmd[1 + u] = b'L',
                            _ => (),
                        }
                    }
                    for u in 0..arm.num_leaf {
                        if state.last_moved[u] {
                            cmd[arm.V + arm.num_joint + u] = b'P';
                        }
                    }
                    for cmd in cmds {
                        state.id = trace.add(cmd, state.id);
                    }
                    next.push(state);
                }
            }
        }
        beam = next;
        if beam.len() == 0 {
            return None;
        }
        crt += 1;
    }
    let mut moves = trace.get(beam[0].id);
    let root = (moves[0][0] as i32, moves[0][1] as i32);
    moves.remove(0);
    Some(Solution { arm, root, moves })
}

/// 評価関数で用いる定数
const C: i64 = 6;
/// 関節が盤面からどれだけはみ出すのを許容するか
const MARGIN: i32 = 2;

/// 全ての辺の回転を再帰的に全列挙し、次状態を生成する
fn all_rots(
    input: &Input,
    arm: &Arm,
    weight: &Vec<i64>,
    cand: &mut BoundedSortedList<i64, (usize, i32, i32, Vec<u8>)>,
    b: usize,
    state: &State,
    used: &mut FastBitSet,
    rot: &mut Vec<u8>,
    ps: &mut Vec<(i32, i32, u8)>,
    u: usize,
) -> bool {
    if u == arm.num_joint {
        let mut eval = state.eval;
        // 同じターンにたこ焼きを取り合わないように変化したマスを記憶しておく
        used.clear();
        for u in 0..arm.num_leaf {
            // 各指先は、1手で掴む・離すことの出来るマスのうちで一番重みが大きいマスに向けて回転する
            let (p, L) = arm.pL[arm.num_joint - 1 + u];
            let (i, j, mut r) = ps[p];
            r = (r + state.rot[arm.num_joint - 1 + u]) & 3;
            let mut best_dir = 0;
            let mut best = 0;
            for dir in 0..4 {
                // 前のターンに稼働しなかった指先は2ターンかけて180度回転することができる
                if dir == 2 && state.last_moved[u] {
                    continue;
                }
                let r = (r + dir) & 3;
                let i = i + DIJ[r as usize].0 * L;
                let j = j + DIJ[r as usize].1 * L;
                if input.on(i, j) {
                    let ij = i as usize * input.N + j as usize;
                    if used[ij] {
                        continue;
                    }
                    if state.holding[u] && !state.s[ij] && input.t[ij]
                        || !state.holding[u] && state.s[ij] && !input.t[ij]
                    {
                        let mut delta = weight[ij];
                        // 未完成のマスが孤立している状態より隣接している状態の方が解きやすいので
                        // 評価関数として Σ_{p:未完成} w[p] - Σ_{pq:隣接する未完成} min(w[p],w[q])/C を用いる
                        for &(di, dj) in &DIJ[..4] {
                            let i2 = i + di;
                            let j2 = j + dj;
                            let ij2 = i2 as usize * input.N + j2 as usize;
                            if input.on(i2, j2) && (state.s[ij2] ^ used[ij2]) != input.t[ij2] {
                                delta -= weight[ij].min(weight[ij2]) / C;
                            }
                        }
                        if best.setmax(delta) {
                            best_dir = dir;
                        }
                    }
                }
            }
            eval -= best;
            rot[arm.num_joint - 1 + u] = (state.rot[arm.num_joint - 1 + u] + best_dir) & 3;
            if best > 0 {
                let r = (r + best_dir) & 3;
                let i = i + DIJ[r as usize].0 * L;
                let j = j + DIJ[r as usize].1 * L;
                let ij = i as usize * input.N + j as usize;
                used.set(ij);
            }
        }
        if eval != state.eval {
            if cand.can_insert(eval) {
                cand.insert(eval, (b, ps[0].0, ps[0].1, rot.clone()));
            }
            true
        } else {
            false
        }
    } else {
        // 再帰的に回転を全列挙
        let mut ok = false;
        for d in 0..4 {
            // 一回休み状態の場合は180度回転も試す
            if d != 2 || state.skip > 0 {
                let (p, L) = arm.pL[u - 1];
                let (i, j, r) = ps[p];
                let r = (rot[u - 1] + r) & 3;
                ps[u] = (i + DIJ[r as usize].0 * L, j + DIJ[r as usize].1 * L, r);
                if -MARGIN <= ps[u].0
                    && ps[u].0 < input.N as i32 + MARGIN
                    && -MARGIN <= ps[u].1
                    && ps[u].1 < input.N as i32 + MARGIN
                {
                    ok |= all_rots(input, arm, weight, cand, b, state, used, rot, ps, u + 1);
                }
            }
            rot[u - 1] = (rot[u - 1] + 1) & 3;
        }
        ok
    }
}

/// 各マスの重みを計算
/// DPにより、各指先がマス(i,j)に存在するようなアームの状態数を求め、その総和の逆数を重みとする
fn get_weight(input: &Input, arm: &Arm) -> Option<Vec<i64>> {
    let mut weight = vec![0; input.N2];
    let N = 2 * MARGIN + 2 + input.N as i32;
    let mut ps = vec![vec![0; (N * N) as usize]; arm.V];
    for ri in 0..input.N as i32 {
        for rj in 0..input.N as i32 {
            ps[0][((ri + MARGIN) * N + rj + MARGIN) as usize] = 1;
        }
    }
    for u in 1..arm.V {
        let (p, L) = arm.pL[u - 1];
        for i in 0..N {
            for j in 0..N {
                if ps[p][(i * N + j) as usize] > 0 {
                    for &(di, dj) in &DIJ[..4] {
                        let i2 = i + di * L;
                        let j2 = j + dj * L;
                        if 0 <= i2 && i2 < N && 0 <= j2 && j2 < N {
                            ps[u][(i2 * N + j2) as usize] += ps[p][(i * N + j) as usize];
                        }
                    }
                }
            }
        }
    }
    let mut tot = vec![0; input.N2];
    for u in arm.num_joint..arm.V {
        for i in 0..input.N {
            for j in 0..input.N {
                tot[i * input.N + j] +=
                    ps[u][((i as i32 + MARGIN) * N + j as i32 + MARGIN) as usize];
            }
        }
    }
    for p in 0..input.N2 {
        if input.s[p] != input.t[p] {
            if tot[p] == 0 {
                return None;
            }
            weight[p] = (1000000000 / tot[p]) * 1000 * C + 1;
        }
    }
    Some(weight)
}

#[derive(Clone, Debug)]
struct Arm {
    V: usize,
    pL: Vec<(usize, i32)>,
    num_joint: usize,
    num_leaf: usize,
}

impl Arm {
    fn new(pL: Vec<(usize, i32)>) -> Self {
        let V = pL.len() + 1;
        let mut is_leaf = vec![true; V];
        for &(p, _) in &pL {
            is_leaf[p] = false;
        }
        let num_joint = is_leaf.iter().filter(|&&b| !b).count();
        Self {
            V,
            pL,
            num_joint,
            num_leaf: V - num_joint,
        }
    }
}

struct Solution {
    arm: Arm,
    root: (i32, i32),
    moves: Vec<Vec<u8>>,
}

#[derive(Clone, Debug)]
struct State {
    s: FixedBitSet,
    rot: Vec<u8>,
    holding: Vec<bool>,
    last_moved: Vec<bool>,
    root: (i32, i32),
    skip: i32,
    eval: i64,
    id: usize,
}

impl State {
    fn new(input: &Input, arm: &Arm, root: (i32, i32), weight: &Vec<i64>) -> Self {
        let mut eval = 0;
        for p in 0..input.N2 {
            if input.s[p] != input.t[p] {
                eval += weight[p];
                if p % input.N + 1 < input.N {
                    if input.s[p + 1] != input.t[p + 1] {
                        eval -= weight[p].min(weight[p + 1]) / C;
                    }
                }
                if p / input.N + 1 < input.N {
                    if input.s[p + input.N] != input.t[p + input.N] {
                        eval -= weight[p].min(weight[p + input.N]) / C;
                    }
                }
            }
        }
        Self {
            s: input.s.clone(),
            rot: vec![0; arm.pL.len()],
            holding: vec![false; arm.V - arm.num_joint],
            last_moved: vec![true; arm.V - arm.num_joint],
            root,
            skip: 0,
            eval,
            id: !0,
        }
    }
}

// 入出力と得点計算

const DIJ: [(i32, i32); 5] = [(0, 1), (1, 0), (0, -1), (-1, 0), (0, 0)];

#[allow(unused)]
struct Input {
    N: usize,
    N2: usize,
    M: usize,
    V: usize,
    s: FixedBitSet,
    t: Vec<bool>,
}

impl Input {
    fn on(&self, x: i32, y: i32) -> bool {
        0 <= x && x < self.N as i32 && 0 <= y && y < self.N as i32
    }
}

fn read_input() -> Input {
    input! {
        N: usize,
        M: usize,
        V: usize,
        s2: [Chars; N],
        t: [Chars; N],
    }
    let mut s = FixedBitSet::with_capacity(N * N);
    for i in 0..N {
        for j in 0..N {
            s.set(i * N + j, s2[i][j] == '1');
        }
    }
    let t = t.into_iter().flatten().map(|c| c == '1').collect();
    Input {
        N,
        N2: N * N,
        M,
        V,
        s,
        t,
    }
}

fn write_output(sol: &Solution) {
    println!("{}", sol.arm.V);
    for &(p, L) in &sol.arm.pL {
        println!("{} {}", p, L);
    }
    println!("{} {}", sol.root.0, sol.root.1);
    let mut moves = sol.moves.clone();
    for t in 1..moves.len() {
        for u in 1..sol.arm.V {
            if moves[t][u] == b'U' {
                assert_eq!(moves[t - 1][u], b'.', "{}/{}", u, sol.arm.num_joint);
                moves[t][u] = b'R';
                moves[t - 1][u] = b'R';
            }
        }
    }
    for cmd in moves {
        println!("{}", String::from_utf8(cmd).unwrap());
    }
}

// ここからライブラリ

pub trait SetMinMax {
    fn setmin(&mut self, v: Self) -> bool;
    fn setmax(&mut self, v: Self) -> bool;
}
impl<T> SetMinMax for T
where
    T: PartialOrd,
{
    fn setmin(&mut self, v: T) -> bool {
        *self > v && {
            *self = v;
            true
        }
    }
    fn setmax(&mut self, v: T) -> bool {
        *self < v && {
            *self = v;
            true
        }
    }
}

#[macro_export]
macro_rules! mat {
	($($e:expr),*) => { vec![$($e),*] };
	($($e:expr,)*) => { vec![$($e),*] };
	($e:expr; $d:expr) => { vec![$e; $d] };
	($e:expr; $d:expr $(; $ds:expr)+) => { vec![mat![$e $(; $ds)*]; $d] };
}

fn get_time() -> f64 {
    static mut STIME: f64 = -1.0;
    let t = std::time::SystemTime::now()
        .duration_since(std::time::UNIX_EPOCH)
        .unwrap();
    let ms = t.as_secs() as f64 + t.subsec_nanos() as f64 * 1e-9;
    unsafe {
        if STIME < 0.0 {
            STIME = ms;
        }
        // ローカル環境とジャッジ環境の実行速度差はget_timeで吸収しておくと便利
        #[cfg(feature = "local")]
        {
            (ms - STIME) * 1.2
        }
        #[cfg(not(feature = "local"))]
        {
            ms - STIME
        }
    }
}

pub struct Trace<T: Clone> {
    log: Vec<(T, usize)>,
}

impl<T: Clone> Trace<T> {
    pub fn new() -> Self {
        Trace { log: vec![] }
    }
    pub fn add(&mut self, c: T, p: usize) -> usize {
        self.log.push((c, p));
        self.log.len() - 1
    }
    pub fn get(&self, mut i: usize) -> Vec<T> {
        let mut out = vec![];
        while i != !0 {
            out.push(self.log[i].0.clone());
            i = self.log[i].1;
        }
        out.reverse();
        out
    }
}

#[derive(Clone, Debug)]
struct Entry<K, V> {
    k: K,
    v: V,
}

impl<K: PartialOrd, V> Ord for Entry<K, V> {
    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
        self.partial_cmp(other).unwrap()
    }
}

impl<K: PartialOrd, V> PartialOrd for Entry<K, V> {
    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
        self.k.partial_cmp(&other.k)
    }
}

impl<K: PartialEq, V> PartialEq for Entry<K, V> {
    fn eq(&self, other: &Self) -> bool {
        self.k.eq(&other.k)
    }
}

impl<K: PartialEq, V> Eq for Entry<K, V> {}

/// K が小さいトップn個を保持
#[derive(Clone, Debug)]
pub struct BoundedSortedList<K: PartialOrd + Copy, V: Clone> {
    que: BinaryHeap<Entry<K, V>>,
    size: usize,
}

impl<K: PartialOrd + Copy, V: Clone> BoundedSortedList<K, V> {
    pub fn new(size: usize) -> Self {
        Self {
            que: BinaryHeap::with_capacity(size),
            size,
        }
    }
    pub fn can_insert(&self, k: K) -> bool {
        self.que.len() < self.size || self.que.peek().unwrap().k > k
    }
    pub fn insert(&mut self, k: K, v: V) {
        if self.que.len() < self.size {
            self.que.push(Entry { k, v });
        } else if let Some(mut top) = self.que.peek_mut() {
            if top.k > k {
                top.k = k;
                top.v = v;
            }
        }
    }
    pub fn list(self) -> Vec<(K, V)> {
        let v = self.que.into_sorted_vec();
        v.into_iter().map(|e| (e.k, e.v)).collect()
    }
    pub fn len(&self) -> usize {
        self.que.len()
    }
}

struct FastBitSet {
    bs: Vec<u32>,
    id: u32,
}

impl FastBitSet {
    fn new(n: usize) -> Self {
        Self {
            bs: vec![0; n],
            id: 1,
        }
    }
    fn clear(&mut self) {
        self.id += 1;
    }
    fn set(&mut self, i: usize) {
        self.bs[i] = self.id;
    }
}

impl std::ops::Index<usize> for FastBitSet {
    type Output = bool;
    fn index(&self, i: usize) -> &bool {
        if self.bs[i] == self.id {
            &true
        } else {
            &false
        }
    }
}

提出情報

提出日時
問題 A - Tree Robot Arm
ユーザ wata_admin
言語 Rust (rustc 1.70.0)
得点 3038
コード長 26294 Byte
結果 AC
実行時間 2931 ms
メモリ 62732 KiB

ジャッジ結果

セット名 test_ALL
得点 / 配点 3038 / 50000000000
結果
AC × 50
セット名 テストケース
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_0000.txt AC 2865 ms 26352 KiB
test_0001.txt AC 2817 ms 6568 KiB
test_0002.txt AC 2888 ms 62732 KiB
test_0003.txt AC 2822 ms 10104 KiB
test_0004.txt AC 2835 ms 8536 KiB
test_0005.txt AC 2756 ms 4568 KiB
test_0006.txt AC 2739 ms 28252 KiB
test_0007.txt AC 2809 ms 5652 KiB
test_0008.txt AC 2843 ms 5924 KiB
test_0009.txt AC 2805 ms 8572 KiB
test_0010.txt AC 2831 ms 6568 KiB
test_0011.txt AC 2808 ms 4356 KiB
test_0012.txt AC 2815 ms 10284 KiB
test_0013.txt AC 2794 ms 5800 KiB
test_0014.txt AC 2826 ms 45736 KiB
test_0015.txt AC 2820 ms 4484 KiB
test_0016.txt AC 2811 ms 11904 KiB
test_0017.txt AC 2817 ms 6716 KiB
test_0018.txt AC 2778 ms 4960 KiB
test_0019.txt AC 2786 ms 20684 KiB
test_0020.txt AC 2815 ms 4132 KiB
test_0021.txt AC 2705 ms 19200 KiB
test_0022.txt AC 2835 ms 7396 KiB
test_0023.txt AC 2798 ms 23808 KiB
test_0024.txt AC 2789 ms 5324 KiB
test_0025.txt AC 2798 ms 48900 KiB
test_0026.txt AC 2810 ms 46100 KiB
test_0027.txt AC 2809 ms 8960 KiB
test_0028.txt AC 2747 ms 9200 KiB
test_0029.txt AC 2836 ms 10556 KiB
test_0030.txt AC 2831 ms 11132 KiB
test_0031.txt AC 2800 ms 4208 KiB
test_0032.txt AC 2786 ms 4336 KiB
test_0033.txt AC 2825 ms 3936 KiB
test_0034.txt AC 2791 ms 10716 KiB
test_0035.txt AC 2758 ms 4568 KiB
test_0036.txt AC 2832 ms 26364 KiB
test_0037.txt AC 2766 ms 44708 KiB
test_0038.txt AC 2757 ms 21576 KiB
test_0039.txt AC 2710 ms 20424 KiB
test_0040.txt AC 2705 ms 39156 KiB
test_0041.txt AC 2825 ms 4452 KiB
test_0042.txt AC 2814 ms 4404 KiB
test_0043.txt AC 2745 ms 5028 KiB
test_0044.txt AC 2765 ms 23740 KiB
test_0045.txt AC 2931 ms 54980 KiB
test_0046.txt AC 2799 ms 7036 KiB
test_0047.txt AC 2739 ms 17872 KiB
test_0048.txt AC 2829 ms 9028 KiB
test_0049.txt AC 2828 ms 56152 KiB