結果

問題 No.5015 Escape from Labyrinth
ユーザー rhoorhoo
提出日時 2023-04-16 17:51:18
言語 Rust
(1.77.0)
結果
AC  
実行時間 2,956 ms / 3,000 ms
コード長 20,681 bytes
コンパイル時間 4,033 ms
コンパイル使用メモリ 182,916 KB
実行使用メモリ 173,124 KB
スコア 224,560
最終ジャッジ日時 2023-04-16 17:56:58
合計ジャッジ時間 302,626 ms
ジャッジサーバーID
(参考情報)
judge16 / judge15
純コード判定しない問題か言語
このコードへのチャレンジ
(要ログイン)

テストケース

テストケース表示
入力 結果 実行時間
実行使用メモリ
testcase_00 AC 2,902 ms
152,720 KB
testcase_01 AC 2,904 ms
159,556 KB
testcase_02 AC 2,907 ms
152,376 KB
testcase_03 AC 2,902 ms
150,632 KB
testcase_04 AC 2,956 ms
152,080 KB
testcase_05 AC 2,900 ms
151,288 KB
testcase_06 AC 2,909 ms
151,908 KB
testcase_07 AC 2,904 ms
151,464 KB
testcase_08 AC 2,903 ms
151,224 KB
testcase_09 AC 2,909 ms
153,844 KB
testcase_10 AC 2,905 ms
152,484 KB
testcase_11 AC 2,904 ms
153,848 KB
testcase_12 AC 2,905 ms
154,536 KB
testcase_13 AC 2,907 ms
155,376 KB
testcase_14 AC 2,908 ms
157,688 KB
testcase_15 AC 2,906 ms
153,900 KB
testcase_16 AC 2,907 ms
161,248 KB
testcase_17 AC 2,905 ms
152,680 KB
testcase_18 AC 2,903 ms
148,668 KB
testcase_19 AC 2,906 ms
150,996 KB
testcase_20 AC 2,906 ms
155,656 KB
testcase_21 AC 2,903 ms
157,124 KB
testcase_22 AC 2,905 ms
152,700 KB
testcase_23 AC 2,901 ms
149,808 KB
testcase_24 AC 2,905 ms
156,712 KB
testcase_25 AC 2,901 ms
156,188 KB
testcase_26 AC 2,903 ms
152,892 KB
testcase_27 AC 2,909 ms
153,980 KB
testcase_28 AC 2,905 ms
151,556 KB
testcase_29 AC 2,901 ms
147,244 KB
testcase_30 AC 2,907 ms
162,172 KB
testcase_31 AC 2,903 ms
152,740 KB
testcase_32 AC 2,899 ms
147,128 KB
testcase_33 AC 2,906 ms
151,840 KB
testcase_34 AC 2,909 ms
157,372 KB
testcase_35 AC 2,906 ms
161,976 KB
testcase_36 AC 2,912 ms
173,124 KB
testcase_37 AC 2,909 ms
160,808 KB
testcase_38 AC 2,906 ms
157,028 KB
testcase_39 AC 2,906 ms
150,280 KB
testcase_40 AC 2,907 ms
157,900 KB
testcase_41 AC 2,901 ms
150,028 KB
testcase_42 AC 2,906 ms
159,084 KB
testcase_43 AC 2,911 ms
156,472 KB
testcase_44 AC 2,907 ms
150,348 KB
testcase_45 AC 2,906 ms
159,404 KB
testcase_46 AC 2,911 ms
162,112 KB
testcase_47 AC 2,908 ms
155,880 KB
testcase_48 AC 2,910 ms
159,040 KB
testcase_49 AC 2,900 ms
142,652 KB
testcase_50 AC 2,904 ms
154,536 KB
testcase_51 AC 2,904 ms
150,152 KB
testcase_52 AC 2,907 ms
152,004 KB
testcase_53 AC 2,908 ms
158,160 KB
testcase_54 AC 2,904 ms
147,372 KB
testcase_55 AC 2,905 ms
157,764 KB
testcase_56 AC 2,907 ms
153,360 KB
testcase_57 AC 2,909 ms
157,648 KB
testcase_58 AC 2,901 ms
156,432 KB
testcase_59 AC 2,906 ms
147,912 KB
testcase_60 AC 2,906 ms
153,316 KB
testcase_61 AC 2,903 ms
149,456 KB
testcase_62 AC 2,902 ms
147,636 KB
testcase_63 AC 2,905 ms
150,696 KB
testcase_64 AC 2,905 ms
154,964 KB
testcase_65 AC 2,906 ms
160,532 KB
testcase_66 AC 2,909 ms
156,152 KB
testcase_67 AC 2,904 ms
155,480 KB
testcase_68 AC 2,906 ms
148,340 KB
testcase_69 AC 2,902 ms
148,412 KB
testcase_70 AC 2,906 ms
156,424 KB
testcase_71 AC 2,902 ms
153,812 KB
testcase_72 AC 2,910 ms
157,428 KB
testcase_73 AC 2,907 ms
155,036 KB
testcase_74 AC 2,903 ms
148,800 KB
testcase_75 AC 2,905 ms
155,412 KB
testcase_76 AC 2,911 ms
170,032 KB
testcase_77 AC 2,903 ms
157,308 KB
testcase_78 AC 2,903 ms
148,608 KB
testcase_79 AC 2,906 ms
155,568 KB
testcase_80 AC 2,906 ms
151,984 KB
testcase_81 AC 2,906 ms
155,124 KB
testcase_82 AC 2,905 ms
152,220 KB
testcase_83 AC 2,906 ms
155,420 KB
testcase_84 AC 2,905 ms
152,084 KB
testcase_85 AC 2,905 ms
153,008 KB
testcase_86 AC 2,904 ms
157,904 KB
testcase_87 AC 2,900 ms
149,416 KB
testcase_88 AC 2,907 ms
155,672 KB
testcase_89 AC 2,907 ms
148,660 KB
testcase_90 AC 2,904 ms
152,084 KB
testcase_91 AC 2,902 ms
150,736 KB
testcase_92 AC 2,887 ms
139,272 KB
testcase_93 AC 2,903 ms
153,256 KB
testcase_94 AC 2,906 ms
150,568 KB
testcase_95 AC 2,901 ms
151,352 KB
testcase_96 AC 2,907 ms
160,336 KB
testcase_97 AC 2,904 ms
153,376 KB
testcase_98 AC 2,904 ms
150,244 KB
testcase_99 AC 2,906 ms
154,264 KB
権限があれば一括ダウンロードができます
コンパイルメッセージ
warning: variable `HP` is assigned to, but never used
   --> Main.rs:471:13
    |
471 |     let mut HP=H;
    |             ^^
    |
    = note: consider using `_HP` instead
    = note: `#[warn(unused_variables)]` on by default

warning: function `simulate` is never used
   --> Main.rs:470:4
    |
470 | fn simulate(input:&In,route:&[usize]){
    |    ^^^^^^^^
    |
    = note: `#[warn(dead_code)]` on by default

warning: 2 warnings emitted

ソースコード

diff #

#![allow(non_snake_case)]
#[cfg(not(local))]macro_rules! eprintln{($($_:tt)*)=>{}}


const MAX_TURN:usize=N*2;

struct BUF{
    base:i64,
    dist:Vec<i64>,
    prev:Vec<Vec<usize>>,
    que:BinaryHeap<Reverse<S<i64,(usize,usize)>>>,
    que0:BinaryHeap<Reverse<S<(i64,i64),(usize,usize)>>>
}
impl BUF{
    fn new()->BUF{
        BUF{
            base:i64::MAX,
            dist:vec![i64::MAX;N2],
            prev:vec![vec![!0;MAX_TURN];N2],
            que:BinaryHeap::new(),
            que0:BinaryHeap::new()
        }
    }
    
    fn reset(&mut self){
        self.base-=100000;
        self.que.clear();
    }
}


// todo
// pinch_penalty
fn score(HP:i64,juwels:usize,key:bool)->f64{
    const KEY_BONUS0:f64=3.;
    const KEY_BONUS1:f64=30.;
    
    juwels as f64
    +if key{
        let r=(1500-HP) as f64/1500.;
        KEY_BONUS0*(KEY_BONUS1/KEY_BONUS0).powf(r)
    } else {
        0.
    }
}


// しょうがないから自明解
fn greedy(input:&In,start:usize,key:bool,seen:&mut [bool])->(usize,Vec<usize>){
    let find_path=|start:usize,goal:usize,seen:&[bool]|->(i64,Vec<usize>){
        let mut prev=vec![!0;N2];
        let mut que=BinaryHeap::new();
        prev[start]=!1;
        que.push(Reverse(S((0,0),start)));
        let mut ans=0;
        'outer: while let Some(Reverse(S((dist,juwels),pos)))=que.pop(){
            for &dd in &DD[..4]{
                let np=pos+dd;
                if np==goal{
                    prev[goal]=pos;
                    ans=juwels;
                    break 'outer;
                }
                let cell=input.grid[np];
                if cell.is_through() && prev[np]==!0{
                    prev[np]=pos;
                    let mut new_j=juwels;
                    if let Juwel(id)=cell{
                        new_j-=!seen[id] as i64;
                    }
                    que.push(Reverse(S((dist+1,new_j),np)));
                }
            }
        }

        let mut route=vec![];
        let mut pos=goal;
        loop{
            route.push(pos);
            pos=prev[pos];
            if pos==!1{
                break;
            }
        }
        route.pop();
        route.reverse();

        (-ans,route)
    };
    if key{
        let mut route=vec![];
        let (score0,mut route0)=find_path(start,input.goal,seen);
        route.append(&mut route0);

        (score0 as usize,route)
    }
    else{
        let mut route=vec![start];
        let (score0,mut route0)=find_path(start,input.key,seen);
        for &pos in &route0{
            if let Juwel(id)=input.grid[pos]{
                seen[id]=true;
            }
        }
        route.append(&mut route0);
        let (score1,mut route1)=find_path(input.key,input.goal,seen);
        route.append(&mut route1);

        ((score0+score1) as usize,route)
    }
}


#[derive(Clone)]
struct Node{
    hist:List<usize>,
    seen:Vec<u8>,
    pos:usize,
    turn:usize,
    HP:i64,
    juwels:usize,
}
impl Node{
    fn new(input:&In)->Node{
        Node{
            hist:Cons(input.start,Rc::new(Nil)),
            seen:vec![0;(input.I>>3)+1],
            pos:input.start,
            turn:0,
            HP:H,
            juwels:0,
        }
    }

    fn get(&self,id:usize)->bool{
        let (a,b)=(id>>3,id&7);
        self.seen[a]>>b&1==1
    }

    fn to_goal(&self,input:&In,best:&mut List<usize>,best_score:&mut usize,BF:&mut BUF){
        assert!(self.get(0));
        
        let old=BF.base;
        BF.reset();
        BF.que0.push(Reverse(S((BF.base,0),(self.pos,0))));
        BF.dist[self.pos]=BF.base;
        BF.prev[self.pos][0]=!0;

        let th=BF.base+self.HP;

        while let Some(Reverse(S((cost,juwels),(pos,t))))=BF.que0.pop(){
            let turn=(self.turn+t)%60;
            let nturn=(turn+1)%60;
            let nt=t+1;
            
            for dd in DX{
                let np=pos+dd;
                let cell=input.grid[np];
                let nc=input.damage[np][nturn]+cost;
                if cell==Goal{
                    let diff=cost-BF.base;
                    if dd==0 || self.HP<=diff{
                        continue;
                    }
                    let mut route=vec![np];
                    let mut pos=(pos,t);
                    loop{
                        route.push(pos.0);
                        pos=(BF.prev[pos.0][pos.1],pos.1-1);
                        if pos.0==!0{
                            break;
                        }
                    }
                    route.pop();
                    route.reverse();
                    
                    let juwels=self.juwels-juwels as usize;

                    assert!(0<self.HP-diff);
                    if *best_score<juwels{
                        *best_score=juwels;
                        *best=self.hist.append(&route);
                    }

                    return;
                }
                else if cell.is_through() && MAX_TURN>nt{
                    if old>BF.dist[np] && dd!=0{
                        continue;
                    }
                    BF.dist[np]=old-1;

                    if (BF.dist[np]>nc || dd==0) && th>nc{
                        BF.dist[np]=BF.dist[np].min(nc);
                        BF.que0.push(Reverse(S((nc,juwels-matches!(cell,Juwel(id) if !self.get(id)) as i64),(np,nt))));
                        BF.prev[np][nt]=pos;
                    }
                }
            }
        }
    }

    fn get_routes(&self,input:&In,BF:&mut BUF)->Vec<(Vec<usize>,i64,Cell)>{
        // todo
        const C:usize=4;
        let R:i64=70.min(self.HP+0);

        let old=BF.base;
        BF.reset();
        let mut ret=vec![];
        BF.que.push(Reverse(S(BF.base,(self.pos,0))));
        BF.dist[self.pos]=BF.base;
        BF.prev[self.pos][0]=!0;
        let th=BF.base+R;

        while let Some(Reverse(S(cost,(pos,t))))=BF.que.pop(){
            let turn=(self.turn+t)%60;
            let nturn=(turn+1)%60;
            let nt=t+1;
            
            for dd in DX{
                let np=pos+dd;
                let cell=input.grid[np];
                let nc=input.damage[np][nturn]+cost;
                if cell==Goal && self.get(0) || matches!(cell,Juwel(id) if !self.get(id)){
                    if dd==0{
                        continue;
                    }
                    let nc=if cell==Goal{cost}else{nc};
                    let mut route=vec![np];
                    let mut pos=(pos,t);
                    loop{
                        route.push(pos.0);
                        pos=(BF.prev[pos.0][pos.1],pos.1-1);
                        if pos.0==!0{
                            break;
                        }
                    }
                    route.pop();
                    route.reverse();
                    ret.push((route,nc-BF.base,cell));
                    if ret.len()>=C{
                        return ret;
                    }
                }
                else if cell.is_through() && 40>nt{ // todo
                    if old>BF.dist[np] && dd!=0{
                        continue;
                    }
                    BF.dist[np]=old-1;

                    if (BF.dist[np]>nc || dd==0) && th>nc{
                        BF.dist[np]=BF.dist[np].min(nc);
                        BF.que.push(Reverse(S(nc,(np,nt))));
                        BF.prev[np][nt]=pos;
                    }
                }
            }
        }
        
        ret
    }

    fn to_cand(&self,input:&In,id:(usize,usize),route:Vec<usize>,diff:i64)->Option<Cand>{
        let HP=self.HP-diff;
        
        let pos=*route.last().unwrap();
        let key=(self.seen[0]&1==1)|(input.id[pos]==0);

        let cost=input.cost(pos,key);
        if HP<=0 || cost>=HP as f64+0.{ // todo
            return None;
        }
        let juwels=self.juwels+1;

        Some(
            Cand{
                parent:id,
                HP,
                juwels,pos,
                turn:self.turn+route.len(),
                score:score(HP,juwels,key),
                op:route
            }
        )
    }
    
    fn append_cands(&self,input:&In,id:(usize,usize),cands:&mut Vec<Vec<Cand>>,best_score:&mut usize,best:&mut List<usize>,BF:&mut BUF){
        let routes=self.get_routes(input,BF);
        
        for (route,diff,cell) in routes{
            if let Some(cand)=self.to_cand(input,id,route,diff){
                if cell==Goal{
                    if *best_score<cand.juwels{
                        assert!(cand.HP>0);
                        *best_score=cand.juwels;
                        *best=self.hist.append(&cand.op);
                    }
                }
                else{
                    cands[cand.HP as usize].push(cand);
                }
            }
        }
    }
}


#[derive(Clone)]
struct Cand{
    parent:(usize,usize),
    op:Vec<usize>,
    pos:usize,
    turn:usize,
    HP:i64,
    juwels:usize,
    score:f64,
}
impl Cand{
    fn to_node(&self,input:&In,parent:&Node)->Node{
        let mut seen=parent.seen.clone();
        if let Juwel(id)=input.grid[self.pos]{
            seen[id>>3]|=1<<(id&7);
        }

        Node{
            seen,
            hist:parent.hist.append(&self.op),
            pos:self.pos,
            turn:self.turn,
            HP:self.HP,
            juwels:self.juwels,
        }
    }
}


struct Beam{
    nodes:Vec<Vec<Node>>,
    best_score:usize,
    best:List<usize>
}
impl Beam{
    fn new(node:Node)->Beam{
        let mut nodes=vec![vec![];H as usize+1];
        nodes[H as usize].push(node);
        Beam{
            nodes,
            best_score:0,
            best:Nil
        }
    }

    fn enum_cands(&mut self,input:&In,HP:usize,cands:&mut Vec<Vec<Cand>>,BF:&mut BUF){
        for (i,node) in self.nodes[HP].iter().enumerate(){
            node.append_cands(input,(HP,i),cands,&mut self.best_score,&mut self.best,BF);
        }
    }

    fn update<'a,I:Iterator<Item=&'a Cand>>(&mut self,input:&In,HP:usize,it:I){
        assert!(self.nodes[HP].is_empty());
        for cand in it{
            let (i,j)=cand.parent;
            let node=cand.to_node(input,&self.nodes[i][j]);
            self.nodes[HP].push(node);
        }
    }
}


fn solve(input:&In)->Vec<usize>{
    let mut M=100;
    
    let mut BF=BUF::new();
    let mut beam=Beam::new(Node::new(input));
    let mut cands=vec![vec![];H as usize+1];

    let mut cnt=vec![0;input.I];

    let mut prev=H as usize;
    let mut time0=get_time();
    
    for i in (1..=H as usize).rev(){
        if H as usize-1000>i && i&15==0 || i==112 || i==80 || i==48 || i==16 || i==8 || i==4 || i==2{
            let time1=get_time();
            if 2.6<=time1{
                M=90;
            }
            else{
                let mut rate=(prev-i) as f64*(2.6-time1)/i as f64/(time1-time0);
                rate=rate.cbrt();
                M=((M as f64*rate) as usize).max(90).min((input.I as f64) as usize);
                eprintln!("# {M}");
                prev=i;
                time0=time1;
            }
        }
        
        beam.enum_cands(input,i,&mut cands,&mut BF);
        if i==1{
            break;
        }

        cands[i-1].sort_unstable_by_key(|cand|Reverse(O(cand.score)));
        cnt.fill(0);
        let th=1; // todo
        let it=cands[i-1].iter().filter(|cand|cnt[input.id[cand.pos]]<th && {cnt[input.id[cand.pos]]+=1; true}).take(M);
        beam.update(input,i-1,it);
    }

    let Beam{mut best_score,mut best,..}=beam;
    eprintln!("end = {:.2}",get_time());

    // 二分探索してできるだけ探索の結果を使おうと努力はする
    if best_score==0{
        let mut ng=1;
        let mut ok=H as usize;

        while ok-ng>1{
            let m=ok+ng>>1;
            let node=&beam.nodes[m][0];

            let mut seen:Vec<_>=(0..input.I).map(|i|node.get(i)).collect();
            let (score,route)=greedy(input,node.pos,node.get(0),&mut seen);
            let score=score+node.juwels;
            let route=node.hist.append(&route);
            if isok(input,&route.to_vec()){
                if best_score<score{
                    best_score=score;
                    best=route;
                }
                ok=m;
            }
            else{
                ng=m;
            }
        }
    }

    'outer: for i in 0..H as usize{
        for node in &beam.nodes[i]{
            if node.get(0){
                node.to_goal(input,&mut best,&mut best_score,&mut BF);
            }
            if get_time()>=2.8{
                eprintln!("# {i}");
                break 'outer;
            }
        }
    }

    eprintln!("score = {}",best_score);
    
    best.to_vec()
}


fn isok(input:&In,route:&[usize])->bool{
    let mut HP=H;
    for (i,p) in route.iter().cloned().enumerate(){
        if i==0 || i==route.len()-1{
            continue;
        }
        HP-=input.damage[p][i%60];
    }
    HP>0
}


fn simulate(input:&In,route:&[usize]){
    let mut HP=H;
    for (i,p) in route.iter().cloned().enumerate(){
        if i==0 || i==route.len()-1{
            continue;
        }
        HP-=input.damage[p][i%60];
    }
    // assert!(HP>0);
    eprintln!("HP = {HP}");
}


fn main(){
    get_time();
    let input=In::input();
    eprintln!("I = {}",input.I);
    let ans=solve(&input);

    #[cfg(local)]{
        simulate(&input,&ans);
    }

    for w in ans.windows(2){
        let id=(0..5).find(|i|DX[*i]==w[1]-w[0]).unwrap();
        println!("{}",DC[id]);
    }

    eprintln!("time = {:.2}",get_time());
}


///////////////////////////////////


#[allow(unused)]
struct Scanner{
    stack:std::str::SplitAsciiWhitespace<'static>
}
#[allow(unused)]
impl Scanner{
    fn new()->Self{
        use std::io::Read;
        let mut tmp=String::new();
        std::io::stdin().read_to_string(&mut tmp).unwrap();
        Self{stack:Box::leak(tmp.into_boxed_str()).split_ascii_whitespace()}
    }

    fn read<T:std::str::FromStr>(&mut self)->T{
        self.stack.next().unwrap().parse::<T>().unwrap_or_else(|_|panic!("parse error {}",std::any::type_name::<T>()))
    }
}


macro_rules! input{
    ($scan:ident, $($rest:tt)*)=>{
        input_inner!{$scan,$($rest)*}
    };
}
 
macro_rules! input_inner{
    ($scan:ident $(,)?)=>{};
    ($scan:ident, $name:ident:$t:tt $($rest:tt)*)=>{
        let $name=read_value!($scan,$t);
        input_inner!{$scan $($rest)*}
    };
}
 
macro_rules! read_value{
    ($scan:ident, ($($t:tt),*))=>{
        ($(read_value!($scan, $t)),*)
    };
    ($scan:ident, [$t:tt;$len:expr])=>{
        (0..$len).map(|_|read_value!($scan,$t)).collect::<Vec<_>>()
    };
    ($scan:ident, Chars)=>{
        read_value!($scan,String).chars().collect::<Vec<char>>()
    };
    ($scan:ident, Usize1)=>{
        read_value!($scan,usize)-1
    };
    ($scan:ident, $t:ty)=>{
        $scan.read::<$t>()
    };
}

use std::collections::*;
use std::cmp::Reverse;
use std::f64::INFINITY;


#[derive(Clone,Copy,PartialEq,Eq,Debug)]
enum Cell{
    Empty,
    Wall,
    Goal,
    Juwel(usize),
    Fire,
    Enemy
}
use Cell::*;
impl Cell{
    fn new(c:char,at:&mut usize)->Cell{
        match c{
            '.'|'S'=>Empty,
            '#'=>Wall,
            'G'=>Goal,
            'K'=>Juwel(0),
            'J'=>{*at+=1; Juwel(*at)},
            'F'=>Fire,
            'E'=>Enemy,
            _=>panic!(),
        }
    }

    fn is_through(&self)->bool{
        !matches!(*self,Wall|Goal|Enemy)
    }

    fn is_block(&self)->bool{
        matches!(self,Wall|Enemy)
    }
}

const N:usize=60;
const N2:usize=(N+2).pow(2);
const H:i64=1500;


fn get_cost(grid:&[Cell],costs:&[f64],start:usize)->Vec<f64>{
    let mut que=BinaryHeap::new();
    que.push(Reverse(S(0.,start)));
    let mut ret=vec![INFINITY;N2];
    ret[start]=0.;

    while let Some(Reverse(S(cost,pos)))=que.pop(){
        for dd in DD{
            let np=pos+dd;
            if ret[np].is_infinite(){
                let nc=if grid[np].is_through(){
                    cost+costs[np]
                }
                else{
                    continue;
                };

                ret[np]=nc;
                que.push(Reverse(S(nc,np)));
            }
        }
    }

    ret
}


struct In{
    I:usize,
    grid:Vec<Cell>,
    id:Vec<usize>,
    start:usize,
    key:usize,
    goal:usize,
    cost_key:Vec<f64>,
    cost_goal:Vec<f64>,
    damage:Vec<Vec<i64>>,
}
impl In{
    fn input()->In{
        let mut scan=Scanner::new();
        input!{
            scan,
            n:usize,
            D:i64,
            h:i64,
            igrid:[Chars;n],
            M:usize,
            ienemy:[(usize,usize,usize);M]
        }

        assert_eq!((n,h),(N,H));
        let mut grid=vec![Wall;N2];
        let mut at=0;
        let mut start=!0;
        let mut key=!0;
        let mut goal=!0;
        
        for i in 1..=N{
            for j in 1..=N{
                let pos=to(i,j);
                let c=igrid[i-1][j-1];
                grid[pos]=Cell::new(c,&mut at);
                if c=='S'{
                    start=pos;
                }
                else if c=='K'{
                    key=pos;
                }
                else if c=='G'{
                    goal=pos;
                }
            }
        }
        assert!(start!=!0 && key!=!0 && goal!=!0);

        let mut enemy=vec![!0;M];
        let mut costf=vec![0.;N2];
        let mut reach=vec![vec![];N2];
        for (id,&(i,j,d)) in ienemy.iter().enumerate(){
            enemy[id]=d;

            let pos=to(i+1,j+1);
            let addf=D as f64/d as f64+1.;
            for dd in DD{
                let mut np=pos+dd;
                while !grid[np].is_block(){
                    costf[np]+=addf;
                    reach[np].push(id);
                    np+=dd;
                }
            }
        }

        let mut damage=vec![vec![0;60];N2];
        for i in 1..=N{
            for j in 1..=N{
                for k in 0..60{
                    let cost=reach[to(i,j)].iter().map(|id|(k%enemy[*id]==0) as i64).sum::<i64>()*D+1;
                    damage[to(i,j)][k]=cost;
                }
            }
        }

        let mut I=0;
        let mut id=vec![!0;N2];
        for i in 1..=N{
            for j in 1..=N{
                if let Juwel(idx)=grid[to(i,j)]{
                    id[to(i,j)]=idx;
                    I+=1;
                }
            }
        }

        In{
            cost_key:get_cost(&grid,&costf,key),
            cost_goal:get_cost(&grid,&costf,goal),
            grid,start,key,goal,
            damage,I,id,
        }
    }

    fn cost(&self,pos:usize,key:bool)->f64{
        if key{
            self.cost_goal[pos]
        }
        else{
            self.cost_goal[self.key]+self.cost_key[pos]
        }
    }
}


fn to(i:usize,j:usize)->usize{
    i*(N+2)+j
}

const DD:[usize;4]=[!0,(N+2).wrapping_neg(),1,N+2];
const DX:[usize;5]=[!0,(N+2).wrapping_neg(),1,N+2,0];
const DC:[&'static str;5]=["M L","M U","M R","M D","S"];


#[derive(PartialEq,PartialOrd)]
struct O<T:PartialEq+PartialOrd>(T);
impl<T:PartialEq+PartialOrd> Eq for O<T>{} 
impl<T:PartialEq+PartialOrd> Ord for O<T>{
    fn cmp(&self,a:&O<T>)->std::cmp::Ordering{
        self.0.partial_cmp(&a.0).unwrap()
    }
}

struct S<T:PartialEq+PartialOrd,U>(T,U);
impl<T:PartialEq+PartialOrd,U> PartialEq for S<T,U>{
    fn eq(&self,a:&S<T,U>)->bool{
        self.0.eq(&a.0)
    }
}
impl<T:PartialEq+PartialOrd,U> PartialOrd for S<T,U>{
    fn partial_cmp(&self,a:&S<T,U>)->Option<std::cmp::Ordering>{
        self.0.partial_cmp(&a.0)
    }
}
impl<T:PartialEq+PartialOrd,U> Eq for S<T,U>{}
impl<T:PartialEq+PartialOrd,U> Ord for S<T,U>{
    fn cmp(&self,a:&S<T,U>)->std::cmp::Ordering{
        self.partial_cmp(a).unwrap()
    }
}


use std::rc::Rc;
#[derive(Clone)]
enum List<T>{
    Cons(T,Rc<List<T>>),
    Nil
}
use List::*;
impl<T:Clone> List<T>{
    fn to_vec(&self)->Vec<T>{
        let mut ret=vec![];
        let mut ptr=self.clone();

        while let Cons(v,prev)=ptr{
            ret.push(v.clone());
            ptr=(*prev).clone();
        }

        ret.reverse();
        ret
    }

    fn append(&self,new:&[T])->List<T>{
        let mut ret=self.clone();
        for n in new{
            ret=Cons(n.clone(),Rc::new(ret));
        }
        ret
    }
}


fn get_time()->f64{
    static mut START:f64=-1.;
    let time=std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH).unwrap().as_secs_f64();
    unsafe{
        if START<0.{
            START=time;
        }

        #[cfg(local)]{
            (time-START)*1.5
        }
        #[cfg(not(local))]{
            time-START
        }
    }
}
0