Submission #39126455
Source Code Expand
extern crate rand;
use rand::Rng;
use std::{
cmp::Reverse,
collections::BinaryHeap,
io::{self, Write},
process,
};
// ---------- Enum, Struct ---------- //
// 方向管理
#[derive(Clone, Copy, PartialEq)]
enum Dir {
None = -1,
Up = 0,
Left = 1,
Down = 2,
Right = 3,
}
// 岩盤の状態管理
#[derive(Clone, Copy, PartialEq)]
enum RockState {
None,
Broken,
NotBroken,
Flowing,
}
// 座標管理
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
struct Point {
x: i32,
y: i32,
}
// ---------- Implementation ---------- //
impl Point {
// コンストラクタ
fn new(x: i32, y: i32) -> Point {
Point { x, y }
}
// マンハッタン距離
#[allow(dead_code)]
fn mdist(&self, other: &Point) -> i32 {
(self.x - other.x).abs() + (self.y - other.y).abs()
}
// ユークリッド距離
fn edist(&self, other: &Point) -> i64 {
((self.x - other.x).pow(2) + (self.y - other.y).pow(2)) as i64
}
}
// ---------- Constants ---------- //
const DX: [i32; 4] = [-1, 0, 1, 0];
const DY: [i32; 4] = [0, -1, 0, 1];
const DX_8: [i32; 8] = [0, 1, 1, 1, 0, -1, -1, -1];
const DY_8: [i32; 8] = [1, 1, 0, -1, -1, -1, 0, 1];
const N: u32 = 200;
// ヤー!パワー!!
//
// ノ从从从ヽ
// /ミミ 彡彡∧
// |/ ̄ ̄ ̄\|
// Y == == Y
// (| ヽ〉ノ |)
// ( | )
// | ノヽノヽ|
// /\(( ̄)//
// / \ ヽ二ノ\
// /⌒\ \ | ヽ
// `/ ヽ \_ノ |
// | | _ |
// | ノ へヽ|
// | )ノ / 三|
// | / ̄ ̄ ̄ _ノ
// 人 /
// \____/
//
const POWER: u32 = 128;
const DFS_WIDTH: i32 = 12; // DFSで探索する間隔幅
const BREAK_AC_INIT: i32 = 3; // 1探索で壊す上限回数
const NEAR_AC: i32 = 15; // 目標地点の近づき度
const FLOWING_AC: i32 = 12; // 流れている岩盤の近づき度
const BROKEN_AC: i32 = 8; // 近くに壊されている岩盤があった時の許容度
// ---------- Functions ---------- //
fn in_range(x: i32, y: i32) -> bool {
x >= 0 && x < N as i32 && y >= 0 && y < N as i32
}
fn query(x: i32, y: i32, p: u32, bedrock: &mut Vec<Vec<(RockState, i32)>>) -> RockState {
// クエリを投げなくていいとき
if bedrock[x as usize][y as usize].0 == RockState::Broken {
return RockState::Broken;
}
if bedrock[x as usize][y as usize].0 == RockState::Flowing {
return RockState::Flowing;
}
// クエリ投げ
println!("{} {} {}", x, y, p);
io::stdout().flush().unwrap();
let mut input = String::new();
io::stdin().read_line(&mut input).unwrap();
let res = input.trim().parse::<i32>().unwrap();
// 回数管理
bedrock[x as usize][y as usize].1 += 1;
// 終わるべき
if res == -1 || res == 2 {
process::exit(0);
}
// 状態
if res == 1 {
bedrock[x as usize][y as usize].0 = RockState::Broken
} else {
bedrock[x as usize][y as usize].0 = RockState::NotBroken
}
// return
bedrock[x as usize][y as usize].0
}
fn query_until_broken(
x: i32,
y: i32,
p: u32,
bedrock: &mut Vec<Vec<(RockState, i32)>>,
nxt_state: RockState,
) {
// 再帰する
let res = query(x, y, p, bedrock);
if res != RockState::Broken && res != RockState::Flowing {
query_until_broken(x, y, p, bedrock, nxt_state);
} else {
bedrock[x as usize][y as usize].0 = nxt_state;
}
}
fn connect_greedy(
src: Point,
target: Point,
bedrock: &mut Vec<Vec<(RockState, i32)>>,
nxt_state: RockState,
) {
// まず src と target を壊す
query_until_broken(src.x, src.y, POWER, bedrock, nxt_state);
query_until_broken(target.x, target.y, POWER, bedrock, nxt_state);
let src_num = bedrock[src.x as usize][src.y as usize].1;
let target_num = bedrock[target.x as usize][target.y as usize].1;
// 一直線に src から target まで掘る
let mut x = src.x;
let mut y = src.y;
while x != target.x || y != target.y {
// もしどっちも1回で壊せていたらパワーを減らしてみる
let power = if src_num == target_num && src_num == 1 {
POWER / 4
} else {
POWER
};
if (x - target.x).abs() > (y - target.y).abs() {
if x < target.x {
query_until_broken(x, y, power, bedrock, nxt_state);
x += 1;
} else {
query_until_broken(x, y, power, bedrock, nxt_state);
x -= 1;
}
} else {
if y < target.y {
query_until_broken(x, y, power, bedrock, nxt_state);
y += 1;
} else {
query_until_broken(x, y, power, bedrock, nxt_state);
y -= 1;
}
}
}
}
fn connect_bfs(src: Point, target: Point, bedrock: &mut Vec<Vec<(RockState, i32)>>) {
// ダイクストラでいい感じにつなげる
let mut que = BinaryHeap::<Reverse<(i32, Point)>>::new();
let mut dist = vec![vec![(1e9 as i32, Point::new(-1, -1)); N as usize]; N as usize];
que.push(Reverse((0, src)));
dist[src.x as usize][src.y as usize].0 = 0;
while let Some(Reverse((dst, now))) = que.pop() {
if now.x == target.x && now.y == target.y {
break;
}
if dst > dist[now.x as usize][now.y as usize].0 {
continue;
}
for i in 0..8 {
let nx = now.x + DFS_WIDTH * DX_8[i];
let ny = now.y + DFS_WIDTH * DY_8[i];
if !in_range(nx, ny) {
continue;
}
// 斜めは倍率を高めに設定
let mag = if DX_8[i].abs() + DY_8[i].abs() > 1 {
3
} else {
1
};
let ndst = dst + (bedrock[nx as usize][ny as usize].1) * mag;
// 壊れてないなら飛ばす
if bedrock[nx as usize][ny as usize].0 != RockState::Broken
&& bedrock[nx as usize][ny as usize].0 != RockState::Flowing
{
continue;
}
if ndst < dist[nx as usize][ny as usize].0 {
dist[nx as usize][ny as usize].0 = ndst;
dist[nx as usize][ny as usize].1 = now;
que.push(Reverse((ndst, Point::new(nx, ny))));
}
}
}
// つなげる
let mut now = target;
while now.x != src.x || now.y != src.y {
let prev = dist[now.x as usize][now.y as usize].1;
if prev.x == -1 {
break;
}
connect_greedy(prev, now, bedrock, RockState::Flowing);
now = prev;
}
}
fn dfs(
now: Point,
prev_dir: Dir,
src: Point,
target: Point,
break_ac: i32,
wsrc: &Vec<Point>,
house: &Vec<Point>,
bedrock: &mut Vec<Vec<(RockState, i32)>>,
visited: &mut Vec<Vec<bool>>,
) -> bool {
let mut rng = rand::thread_rng();
visited[now.x as usize][now.y as usize] = true;
// すでに流れている場合はそこからつなげられる
if bedrock[now.x as usize][now.y as usize].0 == RockState::Flowing {
connect_bfs(src, now, bedrock);
return true;
}
// 近くまで行ったらつなげる
if (now.x - target.x).abs() <= NEAR_AC && (now.y - target.y).abs() <= NEAR_AC {
connect_greedy(now, target, bedrock, RockState::Flowing);
connect_bfs(src, now, bedrock);
return true;
}
// 近くに水路があればそこからつなげる
for di in 0..FLOWING_AC {
for dx in -FLOWING_AC..FLOWING_AC {
for dy in -FLOWING_AC..FLOWING_AC {
if dx.abs() + dy.abs() > di {
continue;
}
let nx = now.x + dx;
let ny = now.y + dy;
if in_range(nx, ny) && bedrock[nx as usize][ny as usize].0 == RockState::Flowing {
connect_greedy(now, Point::new(nx, ny), bedrock, RockState::Flowing);
connect_bfs(src, now, bedrock);
return true;
}
}
}
}
// 目標地点から離れすぎる場合はやめる
let rnddst = ((src.edist(&target) as f64).sqrt() - (now.edist(&target) as f64).sqrt()) / 4.0;
if rnddst.exp() < rng.gen::<f64>() {
return false;
}
// 方向の優先順位決め
let priority_dir: [Dir; 4];
if (now.x - target.x).abs() > (now.y - target.y).abs() {
if now.x > target.x {
if now.y > target.y {
priority_dir = [Dir::Up, Dir::Left, Dir::Right, Dir::Down];
} else {
priority_dir = [Dir::Up, Dir::Right, Dir::Left, Dir::Down];
}
} else {
if now.y > target.y {
priority_dir = [Dir::Down, Dir::Left, Dir::Right, Dir::Up];
} else {
priority_dir = [Dir::Down, Dir::Right, Dir::Left, Dir::Up];
}
}
} else {
if now.y > target.y {
if now.x > target.x {
priority_dir = [Dir::Left, Dir::Up, Dir::Down, Dir::Right];
} else {
priority_dir = [Dir::Left, Dir::Down, Dir::Up, Dir::Right];
}
} else {
if now.x > target.x {
priority_dir = [Dir::Right, Dir::Up, Dir::Down, Dir::Left];
} else {
priority_dir = [Dir::Right, Dir::Down, Dir::Up, Dir::Left];
}
}
}
// 今いる場所から一番近い水源を探す
let mut new_target = wsrc[0];
for i in 1..wsrc.len() {
if now.edist(&wsrc[i]) < now.edist(&new_target) {
new_target = wsrc[i];
}
}
for i in 0..house.len() {
let h = house[i];
if bedrock[h.x as usize][h.y as usize].0 == RockState::Flowing {
if now.edist(&h) < now.edist(&new_target) {
new_target = h;
}
}
}
// DFS
for dir in 0..4 {
let nxt_dir = priority_dir[dir];
// 戻る方向にはいかない
if nxt_dir as i32 % 2 == prev_dir as i32 % 2 && nxt_dir != prev_dir {
continue;
}
let nx = now.x + DFS_WIDTH * DX[nxt_dir as usize];
let ny = now.y + DFS_WIDTH * DY[nxt_dir as usize];
if in_range(nx, ny) {
if visited[nx as usize][ny as usize] {
continue;
}
let mut is_broken_tmp = bedrock[nx as usize][ny as usize].0 == RockState::Broken
|| bedrock[nx as usize][ny as usize].0 == RockState::Flowing;
// 1回の上限を決めて掘る
for _ in 0..break_ac {
let res = query(nx, ny, POWER, bedrock);
if res == RockState::Broken {
is_broken_tmp = true;
break;
}
}
// もし壊れたら先に進む
if is_broken_tmp {
// すでに近くに壊れた岩盤があるなら、そこにつなげる
for di in 0..BROKEN_AC {
for dx in -BROKEN_AC..BROKEN_AC {
for dy in -BROKEN_AC..BROKEN_AC {
if dx.abs() + dy.abs() == 0 {
continue;
}
if dx.abs() + dy.abs() > di {
continue;
}
let nnx = nx + dx;
let nny = ny + dy;
if in_range(nnx, nny) {
if bedrock[nnx as usize][nny as usize].0 == RockState::Broken {
let new_now = Point::new(nx, ny);
let new_src = Point::new(nnx, nny);
// すでに壊れている場所のみをたどって行けるならつなげる
if dfs(
new_src,
Dir::None,
new_src,
target,
0,
wsrc,
house,
bedrock,
visited,
) {
connect_greedy(
new_now,
new_src,
bedrock,
RockState::Flowing,
);
connect_bfs(src, new_now, bedrock);
return true;
}
}
}
}
}
}
let connected = dfs(
Point::new(nx, ny),
nxt_dir,
src,
new_target,
break_ac,
wsrc,
house,
bedrock,
visited,
);
// つながったらtrue
if connected {
return true;
}
}
}
}
false
}
fn input() -> (u32, u32, u32, u32, Vec<Point>, Vec<Point>) {
let (n, w, k, _c) = {
let mut input = String::new();
io::stdin().read_line(&mut input).unwrap();
let mut iter = input.split_whitespace().map(|i| i.parse::<u32>().unwrap());
(
iter.next().unwrap(),
iter.next().unwrap(),
iter.next().unwrap(),
iter.next().unwrap(),
)
};
let wsrc: Vec<Point> = (0..w)
.map(|_| {
let mut input = String::new();
io::stdin().read_line(&mut input).unwrap();
let mut iter = input.split_whitespace().map(|i| i.parse::<i32>().unwrap());
let tmp = Point::new(iter.next().unwrap(), iter.next().unwrap());
tmp
})
.collect();
let house: Vec<Point> = (0..k)
.map(|_| {
let mut input = String::new();
io::stdin().read_line(&mut input).unwrap();
let mut iter = input.split_whitespace().map(|i| i.parse::<i32>().unwrap());
let tmp: Point = Point::new(iter.next().unwrap(), iter.next().unwrap());
tmp
})
.collect();
(n, w, k, _c, wsrc, house)
}
fn main() {
// Constants Assertion
assert!(
DFS_WIDTH > BROKEN_AC,
"常に繋げかねないので、DFS_WIDTH > BROKEN_AC"
);
assert!(
FLOWING_AC > BROKEN_AC,
"処理が変になるので、FLOWING_AC > BROKEN_AC"
);
let (n, w, k, _c, wsrc, house) = input();
// (RockState, i32): (岩盤状態, 何回掘ったか)
let mut bedrock = vec![vec![(RockState::None, 0); n as usize]; n as usize];
// 近い順にソート
let mut pq = BinaryHeap::<Reverse<(i64, u32, u32)>>::new();
for i in 0..k {
let mut nearest = 0u32;
for j in 1..w {
let dist = house[i as usize].edist(&wsrc[j as usize]);
if house[i as usize].edist(&wsrc[nearest as usize]) > dist {
nearest = j;
}
}
pq.push(Reverse((
house[i as usize].edist(&wsrc[nearest as usize]),
i,
nearest,
)));
}
while let Some(Reverse((_, i, nearest_idx))) = pq.pop() {
let h = house[i as usize];
// すでに流れている
if bedrock[h.x as usize][h.y as usize].0 == RockState::Flowing {
continue;
}
// 初期目標地点は一番近いところ
let nearest = if nearest_idx < w {
wsrc[nearest_idx as usize]
} else {
house[(nearest_idx - w) as usize]
};
// すでに壊れている場合はつなぐ
if bedrock[h.x as usize][h.y as usize].0 == RockState::Broken {
connect_bfs(h, nearest, &mut bedrock);
continue;
}
// DFS
let mut visited = vec![vec![false; n as usize]; n as usize];
while !dfs(
h,
Dir::None,
h,
nearest,
BREAK_AC_INIT,
&wsrc,
&house,
&mut bedrock,
&mut visited,
) {
for i in 0..n {
for j in 0..n {
visited[i as usize][j as usize] = false;
}
}
}
// 新しい距離を追加する
for j in 0..k {
if i == j {
continue;
}
let dist = house[i as usize].edist(&house[j as usize]);
pq.push(Reverse((dist, j, w + i)));
}
}
assert!(false, "全部つながってないよ!");
}
Submission Info
| Submission Time |
|
| Task |
A - Excavation |
| User |
a01sa01to |
| Language |
Rust (1.42.0) |
| Score |
6077720 |
| Code Size |
17467 Byte |
| Status |
AC |
| Exec Time |
39 ms |
| Memory |
3864 KiB |
Judge Result
| Set Name |
test_ALL |
| Score / Max Score |
6077720 / 50000000000 |
| 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 |
| Case Name |
Status |
Exec Time |
Memory |
| test_0000.txt |
AC |
26 ms |
3764 KiB |
| test_0001.txt |
AC |
31 ms |
3776 KiB |
| test_0002.txt |
AC |
38 ms |
3796 KiB |
| test_0003.txt |
AC |
25 ms |
3692 KiB |
| test_0004.txt |
AC |
32 ms |
3776 KiB |
| test_0005.txt |
AC |
38 ms |
3660 KiB |
| test_0006.txt |
AC |
34 ms |
3824 KiB |
| test_0007.txt |
AC |
28 ms |
3828 KiB |
| test_0008.txt |
AC |
38 ms |
3828 KiB |
| test_0009.txt |
AC |
28 ms |
3856 KiB |
| test_0010.txt |
AC |
31 ms |
3756 KiB |
| test_0011.txt |
AC |
28 ms |
3700 KiB |
| test_0012.txt |
AC |
28 ms |
3824 KiB |
| test_0013.txt |
AC |
27 ms |
3832 KiB |
| test_0014.txt |
AC |
26 ms |
3712 KiB |
| test_0015.txt |
AC |
27 ms |
3772 KiB |
| test_0016.txt |
AC |
29 ms |
3852 KiB |
| test_0017.txt |
AC |
26 ms |
3824 KiB |
| test_0018.txt |
AC |
24 ms |
3772 KiB |
| test_0019.txt |
AC |
32 ms |
3776 KiB |
| test_0020.txt |
AC |
29 ms |
3848 KiB |
| test_0021.txt |
AC |
36 ms |
3852 KiB |
| test_0022.txt |
AC |
39 ms |
3828 KiB |
| test_0023.txt |
AC |
28 ms |
3788 KiB |
| test_0024.txt |
AC |
31 ms |
3808 KiB |
| test_0025.txt |
AC |
32 ms |
3796 KiB |
| test_0026.txt |
AC |
25 ms |
3704 KiB |
| test_0027.txt |
AC |
24 ms |
3712 KiB |
| test_0028.txt |
AC |
27 ms |
3808 KiB |
| test_0029.txt |
AC |
26 ms |
3712 KiB |
| test_0030.txt |
AC |
25 ms |
3716 KiB |
| test_0031.txt |
AC |
37 ms |
3856 KiB |
| test_0032.txt |
AC |
39 ms |
3808 KiB |
| test_0033.txt |
AC |
29 ms |
3720 KiB |
| test_0034.txt |
AC |
30 ms |
3700 KiB |
| test_0035.txt |
AC |
31 ms |
3732 KiB |
| test_0036.txt |
AC |
32 ms |
3712 KiB |
| test_0037.txt |
AC |
39 ms |
3744 KiB |
| test_0038.txt |
AC |
25 ms |
3820 KiB |
| test_0039.txt |
AC |
38 ms |
3864 KiB |
| test_0040.txt |
AC |
29 ms |
3716 KiB |
| test_0041.txt |
AC |
33 ms |
3768 KiB |
| test_0042.txt |
AC |
37 ms |
3716 KiB |
| test_0043.txt |
AC |
34 ms |
3824 KiB |
| test_0044.txt |
AC |
28 ms |
3712 KiB |
| test_0045.txt |
AC |
25 ms |
3640 KiB |
| test_0046.txt |
AC |
29 ms |
3788 KiB |
| test_0047.txt |
AC |
29 ms |
3716 KiB |
| test_0048.txt |
AC |
28 ms |
3808 KiB |
| test_0049.txt |
AC |
21 ms |
3712 KiB |