Submission #53066906
Source Code Expand
use proconio::input_interactive;
use rand::SeedableRng;
use rand_distr::{Distribution, Normal, WeightedAliasIndex};
use rand_pcg::Pcg64Mcg;
use std::collections::BinaryHeap;
use std::mem::swap;
use std::process::exit;
use num_complex::Complex;
use std::f64::consts::PI;
pub trait ChangeMinMax {
fn chmin(&mut self, x: Self) -> bool;
fn chmax(&mut self, x: Self) -> bool;
}
impl<T: PartialOrd> ChangeMinMax for T {
fn chmin(&mut self, x: T) -> bool {
*self > x && {
*self = x;
true
}
}
fn chmax(&mut self, x: T) -> bool {
*self < x && {
*self = x;
true
}
}
}
const TIME_LIMIT: f64 = 1.95;
const AREA_SIZE: f64 = 1e5;
const MAX_ACCEL: f64 = 5e2;
const GOAL_RADIUS: f64 = 1e3;
const MAX_TURNS: usize = 5000;
const NUM_PARTICLES: usize = 2000;
const FAIL_DECAY: f64 = 0.8;
const TOO_NEAR_DIST: f64 = 2.0 * GOAL_RADIUS;
const VELOCITY_COEFFICIENT: f64 = 0.9;
fn main() {
get_time();
let input = Input::read_input();
let mut controller = Controller::new(&input);
let mut filter = ParticleFilter::new(&input, NUM_PARTICLES);
for turn in 0..MAX_TURNS {
let action = controller.decide_action(turn, &filter);
action.print_action();
match action.action_type {
ActionType::Accel => {
filter.accelerate(&Point::new(action.x as f64, action.y as f64));
},
ActionType::Measure => {
input_interactive! {
distance: f64,
};
filter.update_from_measure(&Point::new(action.x as f64, action.y as f64), distance);
},
}
let move_result = MoveResult::read_move_result();
if controller.update_trips(&move_result) {
break;
}
filter.update_from_move(&move_result, turn);
}
exit(0);
}
struct Input {
wind_std: f64,
measure_std: f64,
start_point: Point,
goals: Vec<Point>,
internal_walls: Vec<Segment>,
walls: Vec<Segment>,
}
impl Input {
fn read_input() -> Self {
input_interactive! {
num_goals: usize,
num_walls: usize,
wind_std: f64,
measure_std: f64,
start_point_f: (f64, f64),
goals_f: [(f64, f64); num_goals],
internal_walls_f: [(f64, f64, f64, f64); num_walls],
};
let start_point = Complex::new(start_point_f.0, start_point_f.1);
let goals = goals_f.into_iter().map(|(x, y)| Point::new(x, y)).collect();
let internal_walls: Vec<Segment> = internal_walls_f.into_iter().map(|(x1, y1, x2, y2)| Segment { a: Point::new(x1, y1), b: Point::new(x2, y2) }).collect();
let surrounding_walls = vec![
Segment {
a: Complex::new(-AREA_SIZE, -AREA_SIZE),
b: Complex::new(-AREA_SIZE, AREA_SIZE)
},
Segment {
a: Complex::new(-AREA_SIZE, AREA_SIZE),
b: Complex::new(AREA_SIZE, AREA_SIZE),
},
Segment {
a: Complex::new(AREA_SIZE, AREA_SIZE),
b: Complex::new(AREA_SIZE, -AREA_SIZE),
},
Segment {
a: Complex::new(AREA_SIZE, -AREA_SIZE),
b: Complex::new(-AREA_SIZE, -AREA_SIZE),
}
];
let mut walls = internal_walls.clone();
walls.extend(surrounding_walls);
Input {
wind_std,
measure_std,
start_point,
goals,
internal_walls,
walls,
}
}
}
enum ActionType {
Accel,
Measure,
}
struct Action {
action_type: ActionType,
x: i32,
y: i32,
}
impl Action {
fn print_action(&self) {
let c = match self.action_type {
ActionType::Accel => 'A',
ActionType::Measure => 'S',
};
println!("{} {} {}", c, self.x, self.y);
}
}
struct MoveResult {
collision: bool,
visited_goals: Vec<usize>,
}
impl MoveResult {
fn read_move_result() -> Self {
input_interactive! {
collision: i32,
num_visited_goals: usize,
visited_goals: [usize; num_visited_goals],
};
MoveResult {
collision: collision == 1,
visited_goals,
}
}
}
struct Trip {
progress: usize,
targets: Vec<Point>,
goal_id: usize,
}
struct Controller {
progress: usize,
trips: Vec<Trip>,
visited: Vec<bool>,
walls: Vec<Segment>,
measured_wall_id: usize,
graph: Graph,
wind_std: f64,
}
impl Controller {
fn new(input: &Input) -> Self {
// make cost matrix
let graph = Graph::new(input);
// solve tsp
let order = solve_tsp(&graph.cost_matrix.into_iter().map(|v| v[0..(input.goals.len() + 1)].to_vec()).collect(), false);
debug_assert_eq!(order.len(), input.goals.len() + 1);
debug_assert_eq!(order[0], input.goals.len());
// make trips
let mut trips = Vec::with_capacity(input.goals.len());
for i in 0..input.goals.len() {
trips.push(Trip {
progress: 0,
targets: graph.paths[order[i]][order[i + 1]].clone(),
goal_id: order[i + 1],
});
}
Controller {
progress: 0,
trips,
visited: vec![false; input.goals.len()],
walls: input.walls.clone(),
measured_wall_id: input.walls.len(),
graph: Graph::new(input),
wind_std: input.wind_std,
}
}
fn decide_action(&mut self, turn: usize, filter: &ParticleFilter) -> Action {
debug_assert!(self.progress < self.trips.len() && !self.trips[self.progress].targets.is_empty());
let average_particle = filter.calc_average();
// todo!("Exploration vs. Exploitation Tradeoff");
if turn % 2 == 0 || self.wind_std == 0.0 {
let (x, y) = trunc_accel(&self.decide_accel(&average_particle));
Action {
action_type: ActionType::Accel,
x,
y,
}
} else {
self.decide_measure(&average_particle)
}
}
fn decide_accel(&mut self, average_particle: &Particle) -> Point {
let pos = average_particle.position;
let vel = average_particle.velocity;
let trip = &mut self.trips[self.progress];
while trip.progress + 1 < trip.targets.len() {
if (pos - trip.targets[trip.progress]).norm() < GOAL_RADIUS {
trip.progress += 1;
} else if !can_move_linearly(&self.walls, &pos, &trip.targets[trip.progress + 1]) {
break;
} else if calc_wall_distance(&self.walls, &Segment { a: pos, b: trip.targets[trip.progress + 1], }) > TOO_NEAR_DIST {
trip.progress += 1;
} else {
break;
}
}
if !can_move_linearly(&self.walls, &pos, &trip.targets[trip.progress]) {
trip.targets = self.graph.calc_path(&self.walls, trip.goal_id, &pos);
trip.progress = 0;
}
let goal = trip.targets[trip.progress] - average_particle.position;
let path = Segment {
a: Point::new(0.0, 0.0),
b: goal,
};
let acc = projection(&path, &vel);
let approaching = dot(&goal, &acc) > 0.0;
let fix = acc - vel;
if fix.norm() >= MAX_ACCEL {
Point::from_polar(MAX_ACCEL, fix.arg())
} else {
let max_proceed = (MAX_ACCEL * MAX_ACCEL - fix.norm_sqr()).sqrt();
let proceed = (self.max_velocity(goal.norm()) + if approaching { -acc.norm() } else { acc.norm() }).clamp(-max_proceed, max_proceed);
fix + Point::from_polar(proceed, goal.arg())
}
}
fn decide_measure(&mut self, average_particle: &Particle) -> Action {
let p = average_particle.position;
let mut min_distance = f64::INFINITY;
let mut measuring_wall_id = self.walls.len();
let mut ret = Action {
action_type: ActionType::Measure,
x: 0,
y: 0,
};
for wall_id in 0..self.walls.len() {
if wall_id == self.measured_wall_id {
continue;
}
let q = calc_nearest_point(&self.walls[wall_id], &p);
if can_move_linearly(&self.walls, &p, &q) {
if min_distance.chmin((q - p).norm()) {
measuring_wall_id = wall_id;
(ret.x, ret.y) = trunc_accel(&Point::from_polar(MAX_ACCEL, (q - p).arg()));
}
}
}
debug_assert!(ret.x != 0 || ret.y != 0);
self.measured_wall_id = measuring_wall_id;
ret
}
fn update_trips(&mut self, move_result: &MoveResult) -> bool {
debug_assert!(self.progress < self.trips.len() && !self.trips[self.progress].targets.is_empty());
for goal_id in move_result.visited_goals.iter() {
debug_assert!(!self.visited[*goal_id]);
self.visited[*goal_id] = true;
}
while self.progress < self.trips.len() {
if self.visited[self.trips[self.progress].goal_id] {
self.progress += 1;
} else {
break;
}
}
self.progress == self.trips.len()
}
fn max_velocity(&self, distance: f64) -> f64 {
// Theoretically, we can accelerate up to arround (2.0 * a * d).sqrt() where a is the acceleration and d is the distance.
if self.wind_std == 0.0 {
VELOCITY_COEFFICIENT * (2.0 * MAX_ACCEL * distance).sqrt()
} else {
VELOCITY_COEFFICIENT * (MAX_ACCEL * distance).sqrt()
}
}
}
struct Graph {
points: Vec<Point>,
cost_matrix: Vec<Vec<i64>>,
paths: Vec<Vec<Vec<Point>>>,
}
impl Graph {
fn new(input: &Input) -> Self {
// It returns the tuple of the cost matrix and its path.
let mut points = input.goals.clone();
points.push(input.start_point);
points.extend(make_waypoints(input));
let mut edges = vec![Vec::new(); points.len()];
for u in 0..points.len() {
for v in 0..points.len() {
if can_move_linearly(&input.walls, &points[u], &points[v]) {
edges[u].push((v, calc_edge_weight(&input.walls, &points[u], &points[v]) as i64));
}
}
}
let n = input.goals.len() + 1;
let inf = i64::max_value() / 2;
let mut cost_matrix = vec![vec![0; points.len()]; n];
let mut paths = vec![vec![Vec::new(); points.len()]; n];
let mut costs = vec![0; points.len()];
let mut from = vec![0; points.len()];
for s in 0..n {
// Dijkstra's algorithm
costs.fill(-inf);
costs[s] = 0;
from.fill(points.len());
let mut todo = BinaryHeap::new();
todo.push((0, s));
while !todo.is_empty() {
let (c, u) = todo.pop().unwrap();
if c < costs[u] {
continue;
}
for (v, w) in edges[u].iter() {
let cost = c - w;
if costs[*v].chmax(cost) {
from[*v] = u;
todo.push((cost, *v));
}
}
}
// restore paths
for t in 0..points.len() {
debug_assert_ne!(costs[t], -inf);
cost_matrix[s][t] = -costs[t];
let mut v = t;
while v != s {
paths[s][t].push(points[v]);
v = from[v];
}
paths[s][t].reverse();
}
}
Graph {
points,
cost_matrix,
paths,
}
}
fn calc_path(&self, walls: &Vec<Segment>, goal_id: usize, p: &Point) -> Vec<Point> {
let mut min_cost = i64::max_value() / 2;
let mut min_cost_v = self.points.len();
for v in 0..self.points.len() {
if !can_move_linearly(walls, p, &self.points[v]) {
continue;
}
if min_cost.chmin(self.cost_matrix[goal_id][v] + calc_edge_weight(walls, p, &self.points[v])) {
min_cost_v = v;
}
}
debug_assert_ne!(min_cost_v, self.points.len());
let mut ret = self.paths[goal_id][min_cost_v].clone();
ret.reverse();
ret.push(self.points[goal_id]);
ret
}
}
fn calc_edge_weight(walls: &Vec<Segment>, p: &Point, q: &Point) -> i64 {
let mut weight = (p - q).norm().sqrt();
let wall_distance = calc_wall_distance(walls, &Segment { a: *p, b: *q, });
if wall_distance < TOO_NEAR_DIST {
// todo!("adjust hyper parameter");
weight *= 1.0 + 0.5 * (TOO_NEAR_DIST / wall_distance.max(1.0)).log2();
}
weight as i64
}
fn make_waypoints(input: &Input) -> Vec<Point> {
let mut waypoints = Vec::new();
for i in 0..(input.internal_walls.len() + input.goals.len() + 1) {
let points =
if i < input.internal_walls.len() {
vec![input.internal_walls[i].a, input.internal_walls[i].b]
} else if i - input.internal_walls.len() < input.goals.len() {
vec![input.goals[i - input.internal_walls.len()]]
} else {
vec![input.start_point]
};
for p in points {
if p.re.abs() == AREA_SIZE || p.im.abs() == AREA_SIZE {
continue;
}
for wall in input.walls.iter() {
if p == wall.a || p == wall.b {
continue;
}
let q = calc_nearest_point(wall, &p);
if can_move_linearly(&input.walls, &p, &q) {
waypoints.push((p + q).scale(0.5));
}
}
}
}
waypoints
}
fn trunc_accel(accel: &Point) -> (i32, i32) {
let x = accel.re.trunc() as i32;
let y = accel.im.trunc() as i32;
debug_assert!(x * x + y * y <= MAX_ACCEL as i32 * MAX_ACCEL as i32);
(x, y)
}
#[derive(Clone)]
struct Particle {
position: Point,
velocity: Point,
likelihood: f64,
}
struct ParticleFilter {
rng: Pcg64Mcg,
num_particles: usize,
particles: Vec<Particle>,
particles_buffer: Vec<Particle>,
measure_std: f64,
goals: Vec<Point>,
walls: Vec<Segment>,
wind_norm: Normal<f64>,
visited: Vec<bool>,
}
impl ParticleFilter {
fn new(input: &Input, num_particles: usize) -> Self {
let particle = Particle {
position: input.start_point,
velocity: Complex::new(0.0, 0.0),
likelihood: 1.0 / num_particles as f64,
};
ParticleFilter {
rng: Pcg64Mcg::seed_from_u64(0),
num_particles,
particles: vec![particle; num_particles],
particles_buffer: Vec::with_capacity(num_particles),
measure_std: input.measure_std,
goals: input.goals.clone(),
walls: input.walls.clone(),
wind_norm: Normal::<f64>::new(0.0, input.wind_std).unwrap(),
visited: vec![false; input.goals.len()],
}
}
fn update_from_measure(&mut self, direction: &Point, measured_distance: f64) {
let mut sum_likelihood = 0.0;
for particle_id in 0..self.particles.len() {
self.particles[particle_id].likelihood *= self.measure_likelihood(&self.particles[particle_id].position, direction, measured_distance);
sum_likelihood += self.particles[particle_id].likelihood;
}
for particle in &mut self.particles {
particle.likelihood /= sum_likelihood;
}
}
fn measure_likelihood(&self, position: &Point, direction: &Point, measured_distance: f64) -> f64 {
let ray = get_ray(position, direction);
let collision_point = calc_collision_point(&self.walls, &ray);
debug_assert_ne!(collision_point, ray.b);
let distance = (collision_point - position).norm().max(EPS);
self.measure_norm_pdf(measured_distance / distance).max(EPS)
}
fn measure_norm_pdf(&self, x: f64) -> f64 {
// You can ignore the constant multiplication because we will normalize it later.
let y = (x - 1.0) / (self.measure_std + EPS);
(-0.5 * y * y).exp()
}
fn accelerate(&mut self, accel: &Point) {
for particle in &mut self.particles {
particle.velocity += accel;
}
}
fn update_from_move(&mut self, move_result: &MoveResult, turn: usize) {
self.particles_buffer.clear();
while self.particles_buffer.len() < self.num_particles {
let weights: Vec<f64> = self.particles.iter().map(|particle| particle.likelihood).collect();
let distribution = WeightedAliasIndex::new(weights).unwrap();
for _ in 0..self.num_particles {
let particle_id = distribution.sample(&mut self.rng);
let mut particle = self.particles[particle_id].clone();
particle.velocity.re += self.wind_norm.sample(&mut self.rng);
particle.velocity.im += self.wind_norm.sample(&mut self.rng);
if self.consistent_with_move_result(&mut particle, move_result) {
self.particles_buffer.push(particle);
if self.particles_buffer.len() == self.num_particles {
break;
}
} else {
self.particles[particle_id].likelihood *= FAIL_DECAY;
}
}
if get_time() > (turn as f64 / MAX_TURNS as f64) * TIME_LIMIT {
if !self.particles_buffer.is_empty() {
break;
} else if !move_result.visited_goals.is_empty() {
let goal = self.goals[move_result.visited_goals[0]];
for particle in &mut self.particles {
particle.position = goal;
self.particles_buffer.push(particle.clone());
}
break;
} else if move_result.collision {
for particle in &mut self.particles {
particle.velocity = Point::new(0.0, 0.0);
self.particles_buffer.push(particle.clone());
}
break;
} else {
// All particles died :(
return;
}
}
}
swap(&mut self.particles, &mut self.particles_buffer);
let likelihood = 1.0 / self.particles.len() as f64;
for particle in &mut self.particles {
particle.likelihood = likelihood;
}
for goal_id in move_result.visited_goals.iter() {
self.visited[*goal_id] = true;
}
}
fn consistent_with_move_result(&self, particle: &mut Particle, move_result: &MoveResult) -> bool {
let path = Segment {
a: particle.position,
b: particle.position + particle.velocity,
};
if path.b.re.abs() < AREA_SIZE && path.b.im.abs() < AREA_SIZE && can_move_linearly(&self.walls, &path.a, &path.b) {
particle.position = path.b;
!move_result.collision && self.calc_visited_goals(&path) == move_result.visited_goals
} else {
particle.velocity = Point::new(0.0, 0.0);
move_result.collision
}
}
fn calc_visited_goals(&self, path: &Segment) -> Vec<usize> {
let mut ret = Vec::new();
for goal_id in 0..self.goals.len() {
if !self.visited[goal_id] && calc_distance(path, &self.goals[goal_id]) <= GOAL_RADIUS {
ret.push(goal_id);
}
}
ret
}
fn calc_average(&self) -> Particle {
let mut position = Point::new(0.0, 0.0);
let mut velocity = Point::new(0.0, 0.0);
for particle in self.particles.iter() {
position += particle.likelihood * particle.position;
velocity += particle.likelihood * particle.velocity;
}
Particle {
position,
velocity,
likelihood: 1.0,
}
}
}
fn can_move_linearly(walls: &Vec<Segment>, p: &Point, q: &Point) -> bool {
let path = Segment {
a: p + Point::from_polar(1.0, (q - p).arg()),
b: q - Point::from_polar(1.0, (q - p).arg()),
};
for wall in walls {
if intersect(&path, wall) {
return false;
}
}
true
}
fn calc_wall_distance(walls: &Vec<Segment>, path: &Segment) -> f64 {
let mut ret = f64::INFINITY;
for wall in walls {
ret.chmin(calc_distance(wall, &path.a));
ret.chmin(calc_distance(wall, &path.b));
ret.chmin(calc_distance(path, &wall.a));
ret.chmin(calc_distance(path, &wall.b));
}
ret
}
fn calc_collision_point(walls: &Vec<Segment>, path: &Segment) -> Point {
let mut max_distance = (path.a - path.b).norm();
let mut ret = path.b;
for segment in walls {
if intersect(segment, path) {
let collision_point = cross_point(segment, path);
let distance = (collision_point - path.a).norm();
if max_distance.chmin(distance) {
ret = collision_point;
}
}
}
ret
}
fn get_ray(position: &Point, direction: &Point) -> Segment {
Segment {
a: *position,
b: position + direction.scale(4.0 * AREA_SIZE / direction.norm()),
}
}
// ------------------
// geometry library
pub type Point = Complex<f64>;
pub const EPS: f64 = 1e-9;
pub fn radian_to_degree(theta: f64) -> f64 {
(180.0 / PI) * theta
}
pub fn degree_to_radian(degree: f64) -> f64 {
(PI / 180.0) * degree
}
// almost equal
pub fn eq(a: f64, b: f64) -> bool {
(b - a).abs() < EPS
}
// angle of b-a-c
pub fn angle(a: &Point, b: &Point, c: &Point) -> f64 {
(c - a).arg() - (b - a).arg()
}
pub fn rot(p: &Point, theta: f64) -> Point {
Point::from_polar(1.0, theta) * p
}
pub fn cross_product(a: &Point, b: &Point) -> f64 {
a.re * b.im - a.im * b.re
}
pub fn dot(a: &Point, b: &Point) -> f64 {
a.re * b.re + a.im * b.im
}
// https://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=CGL_1_C
// positional relationship between b and c from a
pub fn ccw(a: &Point, b: &Point, c: &Point) -> i32 {
let ba = b - a;
let ca = c - a;
if cross_product(&ba, &ca) > EPS {
1 // counter-clockwise
} else if cross_product(&ba, &ca) < -EPS {
-1 // clockwise
} else if dot(&ba, &ca) < 0.0 {
2 // online back (c-a-b)
} else if ba.norm_sqr() < ca.norm_sqr() {
-2 // online front (a-b-c)
} else {
0 // on segment (a-c-b)
}
}
#[derive(Clone, PartialEq)]
pub struct Segment {
a: Point,
b: Point,
}
pub fn projection(s: &Segment, p: &Point) -> Point {
let t = dot(&(p - s.a), &(s.b - s.a)) / (s.b - s.a).norm_sqr();
s.a + (s.b - s.a).scale(t)
}
pub fn on_segment(s: &Segment, p: &Point) -> bool {
ccw(&s.a, &s.b, p) == 0
}
pub fn intersect(s: &Segment, t: &Segment) -> bool {
ccw(&s.a, &s.b, &t.a) * ccw(&s.a, &s.b, &t.b) <= 0 && ccw(&t.a, &t.b, &s.a) * ccw(&t.a, &t.b, &s.b) <= 0
}
pub fn calc_distance(s: &Segment, p: &Point) -> f64 {
let r = projection(s, p);
if dot(&(s.a - r), &(s.b - r)) < 0.0 {
(r - p).norm()
} else {
(s.a - p).norm().min((s.b - p).norm())
}
}
pub fn calc_nearest_point(s: &Segment, p: &Point) -> Point {
let r = projection(s, p);
if dot(&(s.a - r), &(s.b - r)) < 0.0 {
r
} else if (s.a - p).norm_sqr() < (s.b - p).norm_sqr() {
s.a
} else {
s.b
}
}
pub fn cross_point(s: &Segment, t: &Segment) -> Point {
let a = cross_product(&(s.b - s.a), &(t.b - t.a));
let b = cross_product(&(s.b - s.a), &(s.b - t.a));
if eq(a.abs(), 0.0) && eq(b.abs(), 0.0) {
t.a
} else {
t.a + (b / a) * (t.b - t.a)
}
}
// ------------------
// calculate the TSP optimal solution
// start from the last vertex and end to the last vertex
pub fn solve_tsp(cost_matrix: &Vec<Vec<i64>>, back_to_the_start_point: bool) -> Vec<usize> {
if cost_matrix.len() == 1 {
return vec![0];
}
let n = cost_matrix.len() - 1;
let inf = i64::max_value() / 2;
// dp
let mut dp = vec![vec![inf; 1 << n]; n];
for v in 0..n {
dp[v][1 << v] = cost_matrix[n][v];
}
for s in 1..(1 << n) {
for u in 0..n {
if ((s >> u) & 1) == 0 {
continue;
}
for v in 0..n {
if ((s >> v) & 1) == 0 {
let cost = dp[u][s] + cost_matrix[u][v];
dp[v][s | (1 << v)].chmin(cost);
}
}
}
}
let mut last = n;
let mut cost = inf;
let mut s = (1 << n) - 1;
for v in 0..n {
if back_to_the_start_point {
if cost.chmin(dp[v][s] + cost_matrix[v][n]) {
last = v;
}
} else {
if cost.chmin(dp[v][s]) {
last = v;
}
}
}
let mut ret = vec![n; n + 1 + back_to_the_start_point as usize];
ret[n] = last;
s ^= 1 << last;
for i in (1..n).rev() {
for v in 0..n {
if (s & (1 << v)) > 0 && dp[v][s] + cost_matrix[v][last] == dp[last][s | (1 << last)] {
ret[i] = v;
s ^= 1 << v;
break;
}
}
debug_assert_ne!(ret[i], n);
}
ret
}
pub 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;
}
ms - STIME
}
}
Submission Info
| Submission Time |
|
| Task |
B - Windy Drone Control (B) |
| User |
eijirou |
| Language |
Rust (rustc 1.70.0) |
| Score |
563144 |
| Code Size |
27079 Byte |
| Status |
AC |
| Exec Time |
419 ms |
| Memory |
4128 KiB |
Judge Result
| Set Name |
test_ALL |
| Score / Max Score |
563144 / 600000 |
| Status |
|
| 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 |
| Case Name |
Status |
Exec Time |
Memory |
| test_0000.txt |
AC |
351 ms |
3592 KiB |
| test_0001.txt |
AC |
225 ms |
3168 KiB |
| test_0002.txt |
AC |
292 ms |
3684 KiB |
| test_0003.txt |
AC |
182 ms |
3496 KiB |
| test_0004.txt |
AC |
328 ms |
3380 KiB |
| test_0005.txt |
AC |
341 ms |
3808 KiB |
| test_0006.txt |
AC |
230 ms |
3700 KiB |
| test_0007.txt |
AC |
347 ms |
3324 KiB |
| test_0008.txt |
AC |
245 ms |
3648 KiB |
| test_0009.txt |
AC |
257 ms |
3608 KiB |
| test_0010.txt |
AC |
191 ms |
3596 KiB |
| test_0011.txt |
AC |
366 ms |
3492 KiB |
| test_0012.txt |
AC |
225 ms |
3516 KiB |
| test_0013.txt |
AC |
241 ms |
3292 KiB |
| test_0014.txt |
AC |
209 ms |
3304 KiB |
| test_0015.txt |
AC |
212 ms |
3472 KiB |
| test_0016.txt |
AC |
209 ms |
3724 KiB |
| test_0017.txt |
AC |
286 ms |
3324 KiB |
| test_0018.txt |
AC |
347 ms |
4128 KiB |
| test_0019.txt |
AC |
219 ms |
3528 KiB |
| test_0020.txt |
AC |
356 ms |
3880 KiB |
| test_0021.txt |
AC |
320 ms |
3584 KiB |
| test_0022.txt |
AC |
209 ms |
3536 KiB |
| test_0023.txt |
AC |
228 ms |
3544 KiB |
| test_0024.txt |
AC |
239 ms |
3632 KiB |
| test_0025.txt |
AC |
269 ms |
3400 KiB |
| test_0026.txt |
AC |
249 ms |
3372 KiB |
| test_0027.txt |
AC |
259 ms |
3376 KiB |
| test_0028.txt |
AC |
176 ms |
3604 KiB |
| test_0029.txt |
AC |
183 ms |
3612 KiB |
| test_0030.txt |
AC |
241 ms |
3508 KiB |
| test_0031.txt |
AC |
362 ms |
3324 KiB |
| test_0032.txt |
AC |
303 ms |
3492 KiB |
| test_0033.txt |
AC |
292 ms |
3504 KiB |
| test_0034.txt |
AC |
269 ms |
3540 KiB |
| test_0035.txt |
AC |
313 ms |
3560 KiB |
| test_0036.txt |
AC |
262 ms |
3372 KiB |
| test_0037.txt |
AC |
339 ms |
3424 KiB |
| test_0038.txt |
AC |
309 ms |
3428 KiB |
| test_0039.txt |
AC |
247 ms |
3892 KiB |
| test_0040.txt |
AC |
345 ms |
3416 KiB |
| test_0041.txt |
AC |
333 ms |
3664 KiB |
| test_0042.txt |
AC |
188 ms |
3524 KiB |
| test_0043.txt |
AC |
363 ms |
3760 KiB |
| test_0044.txt |
AC |
184 ms |
3388 KiB |
| test_0045.txt |
AC |
225 ms |
3616 KiB |
| test_0046.txt |
AC |
194 ms |
3448 KiB |
| test_0047.txt |
AC |
321 ms |
3568 KiB |
| test_0048.txt |
AC |
167 ms |
3380 KiB |
| test_0049.txt |
AC |
364 ms |
3320 KiB |
| test_0050.txt |
AC |
196 ms |
3636 KiB |
| test_0051.txt |
AC |
276 ms |
3920 KiB |
| test_0052.txt |
AC |
219 ms |
3396 KiB |
| test_0053.txt |
AC |
216 ms |
3576 KiB |
| test_0054.txt |
AC |
277 ms |
3448 KiB |
| test_0055.txt |
AC |
259 ms |
3556 KiB |
| test_0056.txt |
AC |
217 ms |
3292 KiB |
| test_0057.txt |
AC |
230 ms |
3108 KiB |
| test_0058.txt |
AC |
304 ms |
3368 KiB |
| test_0059.txt |
AC |
419 ms |
3544 KiB |