#![allow(non_snake_case)] // ILS fn main(){ get_time(); let input=Input::new(); let mut state=State::new(&input); let mut it=0; while get_time()<=TL{ it+=1; state.a=state.best; // todo: kick let mut n=0; let mut ty=0; while n<4 && ty<10{ let (i,j)=(rnd::get(2),rnd::get(N)); let old=state.a[i][j]; state.a[i][j]&=rnd::next() as usize; n+=(old^state.a[i][j]).count_ones(); ty+=1; } optimize(&input,&mut state); } eprintln!("it = {}",it); eprintln!("skip = {}",unsafe{SKIP}); eprintln!("fail = {:.2}",state.cache.iter().filter(|t|*t.1==-INF).count()); eprintln!("score = {}",state.best_score); state.a=state.best; let ans=state.restore(&input); assert!(state.best_score==get_score(&input,&ans)); for &(i,j) in &ans{ println!("{} {}",i+1,j+1); } eprintln!("time = {}",get_time()); } fn is_valid(input:&Input,is:&[usize],js:&[usize])->bool{ let mut sum=[0;1<Result>{ if !is_valid(input,is,js){ assert!(!restore); unsafe{SKIP+=1;}; return Ok(-INF); } let mut graph=NetworkSimplex::new(2*N*N); for n in 0..N*N{ graph.add_supply(n,1); graph.add_demand(n+N*N,1); let bit=input.dice[n].0; for i in 0..N{ for j in 0..N{ let ok=is[i]|js[j]; if bit&ok==ok{ let mut add=0; for k in bit_iter(is[i]).chain(bit_iter(js[j])){ add+=input.dice[n].1[k] as i64; } graph.add_edge(n,N*N+i*N+j,1,-add); } } } } graph.solve(); if !graph.is_valid(){ assert!(!restore); return Ok(-INF); } if restore{ let mut id=0; let mut pos=vec![]; for n in 0..N*N{ let bit=input.dice[n].0; for i in 0..N{ for j in 0..N{ let ok=is[i]|js[j]; if bit&ok==ok{ if graph.get_flow(id)>0{ pos.push((i,j)); } id+=1; } } } } assert!(pos.len()==N*N); Err(pos) } else{ Ok(-graph.get_total()-3*is.iter().chain(js).map(|i|i.count_ones() as i64).sum::()) } } #[derive(Clone)] struct State{ a:[[usize;N];2], cache:HashMap, best:[[usize;N];2], best_score:i64, } impl State{ fn new(input:&Input)->State{ State{ a:[[0;N];2], cache:Default::default(), best:[[0;N];2], best_score:0, } } fn hash(&self,input:&Input)->u64{ let mut h0=1; let mut h1=1; for i in 0..N{ h0*=input.hash[self.a[0][i]]; h1*=input.hash[self.a[1][i]]; } h0+h1 } fn score(&mut self,input:&Input)->i64{ let hash=self.hash(input); if let Some(&n)=self.cache.get(&hash){ return n; } let ret=solve_flow(input,&self.a[0],&self.a[1],false).unwrap(); self.cache.insert(hash,ret); if self.best_scoreVec<(usize,usize)>{ solve_flow(input,&self.a[0],&self.a[1],true).unwrap_err() } } fn trans_change(input:&Input,state:&mut State,add:f64,score:&mut i64,i:usize,j:usize,new:usize)->bool{ let old=state.a[i][j]; let new=old^1<bool{ let old0=state.a[j0/N][j0%N]; let old1=state.a[j1/N][j1%N]; let mut xor; let mut ty=0; loop{ xor=(old0^old1)&rnd::next() as usize; if xor==0 || xor==old0^old1 && j0/N==j1/N || input.hash[state.a[j0/N][j0%N]^xor]==0 || input.hash[state.a[j1/N][j1%N]^xor]==0{ ty+=1; if ty>=10{ return false; } continue; } break; } state.a[j0/N][j0%N]^=xor; state.a[j1/N][j1%N]^=xor; let new_score=state.score(input); if add<=(new_score-*score) as f64{ *score=new_score; true } else{ state.a[j0/N][j0%N]^=xor; state.a[j1/N][j1%N]^=xor; false } } fn optimize(input:&Input,state:&mut State)->i64{ let mut cand=vec![]; for i in 0..2{ for j in 0..N{ for k in 0..N{ cand.push((i,j,k)); } } } for i in 0..2*N{ for j in 0..2*N{ for _ in 0..2{ // todo cand.push((i,j,!0)); } } } let mut score=state.score(input); let mut stay=0; let mut it=0; while get_time()<=TL{ it+=1; rnd::shuffle(&mut cand); let old=score; for &(i,j,k) in &cand{ if k!=!0{ trans_change(input,state,0.,&mut score,i,j,k); } else{ trans_swap(input,state,0.,&mut score,i,j); } } stay+=(old==score) as usize; if stay>=3{ // todo break; } } // eprintln!("# try {it}"); score } use std::collections::*; const INF:i64=i64::MAX/4; const N:usize=6; const TL:f64=1.95; struct Input{ dice:[(usize,[usize;N]);N*N], hash:[u64;1<Input{ let mut scan=Scanner::new(); input!{ scan, n:[[Usize1;N];N*N], } let mut cnt=[0;1<=6{ hash[i]=rnd::hash(); cand.push(i); } } fast_zeta_superset(&mut cnt); Input{dice,hash,sum:cnt} } } fn get_score(input:&Input,a:&[(usize,usize)])->i64{ let mut ids=[[!0;N];N]; for (t,&(i,j)) in a.iter().enumerate(){ ids[i][j]=t; } let mut score=0; for i in 0..N{ for t in 0..N{ if (0..N).all(|j|input.dice[ids[i][j]].1[t]>0){ let sum=(0..N).map(|j|input.dice[ids[i][j]].1[t]).sum::(); score+=sum-3; } if (0..N).all(|j|input.dice[ids[j][i]].1[t]>0){ let sum=(0..N).map(|j|input.dice[ids[j][i]].1[t]).sum::(); score+=sum-3; } } } score as i64 } #[allow(unused)] 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.4 } #[cfg(not(local))]{ time-START } } } #[macro_export] macro_rules! timer{ ()=>{ #[cfg(local)] let _timer=Timer(get_time()); } } static mut TIME:f64=0.; struct Timer(f64); impl Drop for Timer{ fn drop(&mut self){ unsafe{ TIME+=get_time()-self.0 } } } #[allow(unused)] fn timer_ratio()->f64{ unsafe{TIME/get_time()} } #[allow(unused)] mod rnd { const C:u64=0xcafef00dd15ea5e5; static mut N:u64=C; pub fn set_seed(seed:u64){ unsafe{ N^=seed<<1; } } pub fn next()->u32{ unsafe{ let mut x=N; x^=x>>22; N*=C; (x>>22+(x>>61)) as u32 } } pub fn hash()->u64{ (next() as u64)<<32|next() as u64 } pub fn nextf()->f64{ unsafe{ std::mem::transmute::(0x3ff0000000000000|(next() as u64)<<20)-1. } } pub fn get(n:usize)->usize{ assert!(n<=u32::MAX as usize); next() as usize*n>>32 } pub fn range(a:usize,b:usize)->usize{ assert!(ausize{ assert!(a<=skip && skipi64{ assert!(a(list:&mut [T]){ for i in (0..list.len()).rev(){ list.swap(i,get(i+1)); } } pub fn shuffle_iter(list:&mut [T])->impl Iterator+'_{ (0..list.len()).rev().map(|i|{list.swap(i,get(i+1));list[i]}) } } trait RandomChoice{ type Output; fn choice(&self)->Self::Output; } impl RandomChoice for [T]{ type Output=T; fn choice(&self)->T{ self[rnd::get(self.len())] } } // 自身と同じものは含まない fn bit_subset_iter(n:usize)->impl Iterator{ let mut i=n; std::iter::from_fn(move||{ i=i-1&n; if i==n{ None } else{ Some(i) } }) } struct Scanner{ stack:std::str::SplitAsciiWhitespace<'static> } 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(&mut self)->T{ self.stack.next().unwrap().parse::().unwrap_or_else(|_|panic!("parse error {}",std::any::type_name::())) } } #[macro_export] macro_rules! input{ ($scan:expr $(,)?)=>{}; ($scan:expr, mut $name:ident:$t:tt $($rest:tt)*)=>{ let mut $name=read_value!($scan,$t); input!{$scan $($rest)*} }; ($scan:expr, $name:ident:$t:tt $($rest:tt)*)=>{ let $name=read_value!($scan,$t); input!{$scan $($rest)*} }; } #[macro_export] macro_rules! read_value{ ($scan:expr, ($($t:tt),*))=>{ ($(read_value!($scan, $t)),*) }; ($scan:expr, [$t:tt;$len:expr])=>{ (0..$len).map(|_|read_value!($scan,$t)).collect::>() }; ($scan:expr, [$t:tt])=>{ (0..$scan.read()).map(|_|read_value!($scan,$t)).collect::>() }; ($scan:expr, Chars)=>{ read_value!($scan,String).chars().collect::>() }; ($scan:expr, Usize1)=>{ read_value!($scan,usize)-1 }; ($scan:expr, $t:ty)=>{ $scan.read::<$t>() }; } fn bit_iter(mut n:usize)->impl Iterator{ std::iter::from_fn(move|| if n==0{ None } else{ let i=n.trailing_zeros() as usize; n^=1<, prev:Vec, } impl LinkedList{ fn new(l:usize,n:usize)->LinkedList{ LinkedList{ n, next:(0..n).map(|_|0).chain(n..n+l).collect(), prev:(0..n).map(|_|0).chain(n..n+l).collect(), } } fn rep(&self,l:usize)->usize{ l+self.n } fn head(&self,l:usize)->usize{ self.next[self.rep(l)] } fn tail(&self,l:usize)->usize{ self.prev[self.rep(l)] } fn link(&mut self,u:usize,v:usize){ self.next[u]=v; self.prev[v]=u; } fn push(&mut self,l:usize,n:usize){ self.link(self.tail(l),n); self.link(n,self.rep(l)); } fn erase(&mut self,n:usize){ self.link(self.prev[n],self.next[n]) } } #[derive(Clone,Default)] struct Node{ parent:usize, pred:usize, supply:Flow, pot:Cost } #[derive(Clone)] pub struct Edge{ node:(usize,usize), cap:Flow, cost:Cost, flow:Flow, state:i8 } pub struct NetworkSimplex{ v:usize, e:usize, next_arc:usize, block_size:usize, node:Vec, edge:Vec, childs:LinkedList, bfs:Vec } impl NetworkSimplex{ pub fn new(v:usize)->NetworkSimplex{ NetworkSimplex{ v,e:0, next_arc:0, block_size:0, node:vec![Node::default();v+1], edge:vec![], childs:LinkedList::new(0,0), bfs:vec![] } } pub fn add_supply(&mut self,u:usize,supply:Flow){ self.node[u].supply+=supply; } pub fn add_demand(&mut self,u:usize,supply:Flow){ self.node[u].supply-=supply; } // idは順番に振られる pub fn add_edge(&mut self,u:usize,v:usize,cap:Flow,cost:Cost)->usize{ assert!(uFlow{ self.edge[e].flow } pub fn get_supply(&self,n:usize)->Flow{ self.get_flow(self.e+n) } pub fn get_demand(&self,n:usize)->Flow{ -self.get_flow(self.e+n) } pub fn get_total(&self)->CostSum{ (0..self.e).map(|e|self.edge[e].cost as CostSum*self.edge[e].flow as CostSum).sum() } pub fn is_valid(&self)->bool{ (self.e..self.edge.len()).all(|e|self.edge[e].flow<=0) } fn reduced_cost(&self,e:usize)->Cost{ let (u,v)=self.edge[e].node; self.edge[e].cost+self.node[u].pot-self.node[v].pot } fn select_pivot_edge(&mut self)->usize{ let mut min=0; let mut in_arc=!0; let mut cnt=self.block_size; for _ in 0..self.edge.len(){ let next=self.edge[self.next_arc].state as Cost*self.reduced_cost(self.next_arc); if next