nagissさんの作品(ソースコード)

//------------------------------------------------------------------------------
/// @file
/// @author   ハル研究所プログラミングコンテスト実行委員会
///
/// @copyright  (C)HAL Laboratory, Inc.
/// @attention  このファイルの利用は、同梱のREADMEにある
///             利用条件に従ってください。
//------------------------------------------------------------------------------

/*
概要: 
  1. 初期地点と各巻物の座標からダイクストラ法をして雑に巻物間ターン数を見積もる (16 秒)
  2. 見積もったターン数を使って、巻物を回収する順番を巡回セールスマン問題の要領で計算 (8 秒)
  3. 順番に従って、A* 探索で最終的な経路を決定 (35 秒)

ダイクストラ法と A* 探索では、現在地点から直進して辿り着ける場所を隣接ノードとして扱う
*/

#include <cmath>
#include <array>
#include <queue>
#include <vector>
#include <chrono>
#include <numeric>
#include <iostream>
#include <algorithm>
#include "Answer.hpp"

//#define PRINT_DEBUG

using namespace std;

//------------------------------------------------------------------------------

namespace hpc {


// >>> ユーティリティ >>>

template<class T> void print_2d_array(const T& arr) {
#ifdef PRINT_DEBUG
	for (const auto& ar : arr) {
		for (const auto& a : ar) {
			cerr << a << " ";
		}
		cerr << "\n" << endl;
	}
#endif
}

using Meter = short;
using RegionId = short;
using AreaId = short;
using Angle = int;
using PosId = int;

template<typename T> inline bool neq(const T& a, const T& b) {
	if (sizeof(T) == sizeof(double)) {
		return *(long long*)&a != *(long long*)&b;
	} else {
		return a != b;
	}
}
template<typename T> inline bool eq(const T& a, const T& b) {
	if (sizeof(T) == sizeof(double)) {
		return *(long long*)&a == *(long long*)&b;
	} else {
		return a == b;
	}
}

template<typename T> struct Vec2 {
	T x, y;
	constexpr Vec2() {}
	constexpr Vec2(const T& aX, const T& aY) : x(aX), y(aY) {}
	constexpr Vec2(const Vector2& v) : x(v.x), y(v.y) {}
	template<typename S> constexpr Vec2(const Vec2<S>& v) : x((T)v.x), y((T)v.y) {}
	inline Vec2 operator+(const Vec2& rhs) const {
		return Vec2(x + rhs.x, y + rhs.y);
	}
	inline Vec2 operator+(const T& rhs) const {
		return Vec2(x + rhs, y + rhs);
	}
	inline Vec2 operator-(const Vec2& rhs) const {
		return Vec2(x - rhs.x, y - rhs.y);
	}
	template<typename S> inline Vec2 operator*(const S& rhs) const {
		return Vec2(x * rhs, y * rhs);
	}
	inline Vec2& operator+=(const Vec2& rhs) {
		x += rhs.x;
		y += rhs.y;
		return *this;
	}
	inline bool operator!=(const Vec2& rhs) const {
		if (sizeof(x) == sizeof(double)) {
			return neq(x, rhs.x) || neq(y, rhs.y);
		} else {
			return x != rhs.x || y != rhs.y;
		}
	}
	inline bool operator==(const Vec2& rhs) const {
		if (sizeof(x) == sizeof(double)) {
			return eq(x, rhs.x) && eq(y, rhs.y);
		} else {
			return x == rhs.x && y == rhs.y;
		}
	}
	inline operator Vector2() const {
		return Vector2((float)x, (float)y);
	}
	inline double l2_norm() const {
		return sqrt(x * x + y * y);
	}
	inline double l2_norm_square() const {
		return x * x + y * y;
	}
};

template<typename T, typename S> inline Vec2<T> operator*(const S& lhs, const Vec2<T>& rhs) {
	return rhs * lhs;
}
template<typename T> ostream& operator<<(ostream& os, const Vec2<T>& vec) {
	os << vec.y << ' ' << vec.x;
	return os;
}

struct Rectangle {
	Meter d, r, u, l;
	Rectangle(const Meter& D, const Meter& R, const Meter& U, const Meter& L) :
		d(D), r(R), u(U), l(L) {}
};


// >>> 定数 >>>
constexpr double PI = 3.141592653589793;
constexpr array<Vec2<int>, 4> Dxy{ Vec2<int>{ 0, 1 }, { 1, 0 }, { 0, -1 }, { -1, 0 } };  // DRUL
constexpr double EPS = 1e-3;
constexpr int DIVISION_SIZE = 1;
constexpr double EPS_TERRAIN_COEF = 1e-8;
constexpr array<double, 4> TERRAIN_COEF = { 1.0 - EPS_TERRAIN_COEF, 0.6 - EPS_TERRAIN_COEF, 0.3 - EPS_TERRAIN_COEF, 0.1 - EPS_TERRAIN_COEF };

using AStarPosId = int;
constexpr int A_STAR_DIVISION_SIZE = 5;
constexpr AStarPosId A_STAR_N_INTERMEDIATE = 50 * 50 * A_STAR_DIVISION_SIZE * A_STAR_DIVISION_SIZE;
constexpr AStarPosId A_STAR_N_Y_INT = 50 * 50 * A_STAR_DIVISION_SIZE;
constexpr AStarPosId A_STAR_N_NORMAL = A_STAR_N_INTERMEDIATE + A_STAR_N_Y_INT * 2;
constexpr AStarPosId A_STAR_N_STARTS = 250;
// <<< 定数 <<<


// >>> ステージ情報等 >>>
double T0;
int stage_num;
int n_scrolls;
array<Vec2<Meter>, 20> scroll_poses;

Meter scroll_distance[20][20];
Meter scroll_distance_from_start[20];

array<array<RegionId, 50>, 50> region_ids;  // 座標 -> 連結成分の ID
vector<Terrain> region_terrains;  // 連結成分の ID -> 地形

array<array<RegionId, 50>, 50> area_ids;  // 座標 -> 長方形の ID
vector<Rectangle> area_rectangles;  // 長方形 ID -> DRUL
// <<< ステージ情報等 <<<


// 時間
inline double time() {
	return static_cast<double>(chrono::duration_cast<chrono::nanoseconds>(chrono::steady_clock::now().time_since_epoch()).count()) / 1000000000.0;
}

// 整数判定
inline bool is_integer(const double& x) {
	return eq((double)x, (double)(int)x);
}

// sin, cos の前計算
array<double, 8192> sin_table, cos_table;
void init_sin_cos_table() {
	constexpr double eps = 1e-4;
	sin_table[0] = sin(0.0 + eps);
	cos_table[0] = cos(0.0 + eps);
	for (int base = 1; base < (int)sin_table.size(); base <<= 1) {
		for (int i = 0; i < base; i++) {
			sin_table[base + i] = sin(2 * PI * ((double)i + 0.5) / base + eps);
			cos_table[base + i] = cos(2 * PI * ((double)i + 0.5) / base + eps);
		}
	}
}

// 2 次元 array をひとつの値で埋める関数
template<class T, size_t s1, size_t s2>
inline void fill_2d_array(array<array<T, s2>, s1>& arr, const T& val) {
	fill((T*)arr.data(), (T*)(arr.data() + arr.size()), val);
}

// <<< ユーティリティ <<<


// >>> 関数いろいろ >>>

// まっすぐ進んで地形の境界をまたぐ場所を取得する関数
Vec2<double> go_straight(const Vec2<double>& pos, const Angle& angle, const RegionId& region_id) {
	// 境界を伝う場合は無理なので別途処理
	// 遅い地形から出るときはすぐ止まる

	const Vec2<double> e(cos_table[angle], sin_table[angle]);  // 移動方向への単位ベクトル
	const Vec2<double> e_inv(1.0 / e.x, 1.0 / e.y);
	const int x_direction = e.x > 0.0 ? 1 : -1;
	const int y_direction = e.y > 0.0 ? 1 : -1;
	Vec2<double> p = pos;  // 現在地点
	Vec2<int> int_p((int)p.x, (int)p.y);

	// すぐ止まる場合
	if (is_integer(pos.y)) {
		if (y_direction == 1) {
			if (region_id != region_ids[int_p.y][int_p.x]) return pos;
		} else {
			if (region_id != region_ids[int_p.y - 1][int_p.x]) return pos;
		}
	}
	if (is_integer(pos.x)) {
		if (x_direction == 1) {
			if (region_id != region_ids[int_p.y][int_p.x]) return pos;
		} else {
			if (region_id != region_ids[int_p.y][int_p.x - 1]) return pos;
		}
	}

	AreaId area_id = area_ids[int_p.y][int_p.x];
	Vec2<Meter> next(e.x > 0.0 ? area_rectangles[area_id].r : area_rectangles[area_id].l,
		e.y > 0.0 ? area_rectangles[area_id].d : area_rectangles[area_id].u);
	bool over_x = false;  // 境界の x 軸を越えて止まったかどうか

	while (true) {  // 方向に従って進む
		double dist_y = ((double)next.y - p.y) * e_inv.y;
		double dist_x = ((double)next.x - p.x) * e_inv.x;
		if (dist_y < dist_x) {  // 縦に超えた場合
			over_x = false;
			p += (dist_y + 1e-4) * e;
		} else {  // 横に越えた場合
			over_x = true;
			p += (dist_x + 1e-4) * e;
		}
		int_p = Vec2<int>(p);
		if (p.y < 0.0) int_p.y = -1;  // キャストしたときに -0.5 が 0 になってしまうのを補正
		if (p.x < 0.0) int_p.x = -1;
		if (y_direction == 1) { if (int_p.y == 50) break; } else { if (int_p.y == -1) break; }
		if (x_direction == 1) { if (int_p.x == 50) break; } else { if (int_p.x == -1) break; }
		area_id = area_ids[int_p.y][int_p.x];
		next = Vec2<Meter>(x_direction > 0.0 ? area_rectangles[area_id].r : area_rectangles[area_id].l,
			y_direction > 0.0 ? area_rectangles[area_id].d : area_rectangles[area_id].u);
		if (region_ids[int_p.y][int_p.x] != region_id) {  // 進んだ先が領域外(・進む前は領域内)
			break;
		}
	}  // end while
	p += -(1e-4) * e;
	if (over_x) {
		if (x_direction == 1) {
			//HPC_ASSERT(abs(p.x - (double)int_p.x) < EPS);
			p.x = (double)int_p.x;
		} else {
			//HPC_ASSERT(abs(p.x - (double)int_p.x - 1.0) < EPS);
			p.x = (double)int_p.x + 1.0;
		}
	} else {
		if (y_direction == 1) {
			//HPC_ASSERT(abs(p.y - (double)int_p.y) < EPS);
			p.y = (double)int_p.y;
		} else {
			//HPC_ASSERT(abs(p.y - (double)int_p.y - 1.0) < EPS);
			p.y = (double)int_p.y + 1.0;
		}
	}
	//HPC_ASSERT(0.0 <= p.x && p.x <= 50.0 && 0.0 <= p.y && p.y <= 50.0);
	return p;
}

template<class T, int max_size> struct Queue {
	array<T, max_size> data;
	int left, right;
	Queue() : data(), left(0), right(0) {}
	inline bool empty() const {
		return left == right;
	}
	inline void push(const T& value) {
		data[right] = value;
		right++;
	}
	inline void pop() {
		left++;
	}
	inline T front() const {
		return data[left];
	}
	template <class... Args> inline void emplace(const Args&... args) {
		data[right] = T(args...);
		right++;
	}
	inline void clear() {
		left = 0;
		right = 0;
	}
};

Queue<pair<
	pair<Angle, Vec2<double>>,
	pair<Angle, Vec2<double>>
>, 4096> q_boundary;

inline int ceil_int(const double& val) {
	int int_val = (int)val;
	return val == (double)int_val ? int_val : ++int_val;
}

// まっすぐ進んで行ける場所を取得
template<bool start_y_boundary, bool start_x_boundary, bool a_star>
vector<pair<Meter, Vec2<double>>> _get_boudary(Vec2<double> pos, const RegionId& region_id, const double& jump_power) {
	constexpr int angle_limit = a_star ? 256 : 128;
	constexpr int force_angle_max = a_star ? 64 : 8;
	constexpr int division_size = a_star ? A_STAR_DIVISION_SIZE : DIVISION_SIZE;
	vector<pair<Meter, Vec2<double>>> res;
	res.reserve(300);
	q_boundary.clear();
	Vec2<int> int_pos((int)pos.x, (int)pos.y);

	const Terrain& terrain = region_terrains[region_id];
	double jump_dist = jump_power * TERRAIN_COEF[(int)terrain];
	RegionId outer_region_id;
	double outer_jump_dist;

	// 境界に沿って進む
	if (start_y_boundary || start_x_boundary) {
		if (start_y_boundary) {
			if (start_x_boundary) return res;  // 格子点は面倒なので  // 実はそんなに面倒じゃないかも
			//HPC_ASSERT((region_id == region_ids[int_pos.y][int_pos.x]) || (region_id == region_ids[int_pos.y - 1][int_pos.x]));
			if (region_id != region_ids[int_pos.y][int_pos.x]) outer_region_id = region_ids[int_pos.y][int_pos.x];
			else outer_region_id = region_ids[int_pos.y - 1][int_pos.x];
			outer_jump_dist = jump_power * TERRAIN_COEF[(int)region_terrains[outer_region_id]];
			for (int i = 0; (double)i / (double)DIVISION_SIZE < outer_jump_dist; i++) {  // 敢えて DIVISION_SIZE にしてる
				Vec2<double> p = Vec2<double>(pos.x + (outer_jump_dist - (double)i / (double)DIVISION_SIZE), pos.y);
				if (0.5 <= p.x && p.x <= 49.5 && region_ids[int_pos.y][(int)p.x] != region_ids[int_pos.y - 1][(int)p.x]) {
					res.emplace_back(1, p);
				}
				p = Vec2<double>(pos.x - (outer_jump_dist - (double)i / (double)DIVISION_SIZE), pos.y);
				if (0.5 <= p.x && p.x <= 49.5 && region_ids[int_pos.y][(int)p.x] != region_ids[int_pos.y - 1][(int)p.x]) {
					res.emplace_back(1, p);
				}
			}
		} else {
			//HPC_ASSERT((region_id == region_ids[int_pos.y][int_pos.x]) || (region_id == region_ids[int_pos.y][int_pos.x - 1]));
			if (region_id != region_ids[int_pos.y][int_pos.x]) outer_region_id = region_ids[int_pos.y][int_pos.x];
			else outer_region_id = region_ids[int_pos.y][int_pos.x - 1];
			outer_jump_dist = jump_power * TERRAIN_COEF[(int)region_terrains[outer_region_id]];
			for (int i = 0; (double)i / (double)DIVISION_SIZE < outer_jump_dist; i++) {
				Vec2<double> p = Vec2<double>(pos.x, pos.y + (outer_jump_dist - (double)i / (double)DIVISION_SIZE));
				if (0.5 <= p.y && p.y <= 49.5 && region_ids[(int)p.y][int_pos.x] != region_ids[(int)p.y][int_pos.x - 1]) {
					res.emplace_back(1, p);
				}
				p = Vec2<double>(pos.x, pos.y - (outer_jump_dist - (double)i / (double)DIVISION_SIZE));
				if (0.5 <= p.y && p.y <= 49.5 && region_ids[(int)p.y][int_pos.x] != region_ids[(int)p.y][int_pos.x - 1]) {
					res.emplace_back(1, p);
				}
			}
		}
		//HPC_ASSERT(jump_dist <= outer_jump_dist);
	}
	// angle == 0 のときは面倒なので省略
	{
		// const auto p = go_straight(pos, 0, region_id);  // これやると後が面倒
		q_boundary.emplace(make_pair(0, Vec2<double>(99.9, 99.9)), make_pair(0, Vec2<double>(99.9, 99.9)));
	}
	while (!q_boundary.empty()) {
		const auto v = q_boundary.front();
		q_boundary.pop();
		const auto& left = v.first;
		const auto& right = v.second;
		const Angle angle = left.first >= right.first ? left.first * 2 + 1 : right.first * 2;  // 中央の方角
		const Vec2<double> e(cos_table[angle], sin_table[angle]);  // 移動方向への単位ベクトル
		Vec2<double> p;
		if ((left.second - right.second).l2_norm_square() > 1.0 || angle < 4) {  // > 1.0 なら確実
			p = go_straight(pos, angle, region_id);
		} else {  // 左右の間隔が狭いとき、処理省略
			if (is_integer(left.second.x) && is_integer(right.second.x)) {
				p = Vec2<double>(left.second.x, pos.y + e.y * (left.second.x - pos.x) / e.x);
			} else if (is_integer(left.second.y) && is_integer(right.second.y)) {
				p = Vec2<double>(pos.x + e.x * (left.second.y - pos.y) / e.y, left.second.y);
			} else {
				p = go_straight(pos, angle, region_id);
			}
		}
		if (!start_x_boundary && !start_y_boundary) {
			//HPC_ASSERT(neq(p, pos));  // ほんまか?
		}
		if (neq(p, pos) && p.x >= 0.5 && p.x <= 49.5 && p.y >= 0.5 && p.y <= 49.5) {  // フィールドの端でない場合
			// ふちで止まる場合
			double dist = (p - pos).l2_norm();
			int n_jumps;
			if (!start_x_boundary && !start_y_boundary) {
				n_jumps = (int)ceil(dist / jump_dist);  // なんか遅い?
			} else {
				if (outer_jump_dist >= dist) n_jumps = 1;
				else n_jumps = 1 + (int)ceil((dist - outer_jump_dist) / jump_dist);
			}
			
			res.emplace_back(n_jumps, p);

			// ふちで止まらない場合: 再遠点  // 速 -> 遅 の場合は省略
			Vec2<double> top;
			if (!start_x_boundary && !start_y_boundary) {
				top = pos + (double)n_jumps * jump_dist * e;
			} else {
				top = pos + (outer_jump_dist + (double)(n_jumps - 1) * jump_dist) * e;
			}
			if (0.5 <= top.x && top.x <= 49.5 && 0.5 <= top.y && top.y <= 49.5
				&& (int)region_terrains[region_ids[(int)top.y][(int)top.x]] <= (int)terrain
				) {
				res.emplace_back(n_jumps, top);
			}

			// ふちで止まらない場合: ふちの先の別の境界
			for (double y = floor(min(p.y, top.y)) + 1.0; y < max(p.y, top.y); y++) {
				//HPC_ASSERT(is_integer(y));
				double x = pos.x + e.x * (y - pos.y) / e.y;  // y 軸を越えないとこの行に到達し得ないので e.y != 0.0
				if (0.5 <= x && x <= 49.5 && 0.5 <= y && y <= 49.5) {
					if (region_terrains[region_ids[(int)y][(int)x]] != region_terrains[region_ids[(int)y - 1][(int)x]]) {
						res.emplace_back(n_jumps, Vec2<double>(x, y));
						//HPC_ASSERT((Vec2<double>(x, y) - pos).l2_norm() < n_jumps * jump_power * 1.01);
					}
				}
			}
			for (double x = floor(min(p.x, top.x)) + 1.0; x < max(p.x, top.x); x++) {
				//HPC_ASSERT(is_integer(x));
				double y = pos.y + e.y * (x - pos.x) / e.x;  // x 軸を越えないとこの行に到達し得ないので e.x != 0.0
				if (0.5 <= x && x <= 49.5 && 0.5 <= y && y <= 49.5) {
					if (region_terrains[region_ids[(int)y][(int)x]] != region_terrains[region_ids[(int)y][(int)x - 1]]) {
						res.emplace_back(n_jumps, Vec2<double>(x, y));
						//HPC_ASSERT((Vec2<double>(x, y) - pos).l2_norm() < n_jumps * jump_power * 1.01);
					}
				}
			}
		}
		if (angle < angle_limit) {
			if (angle < force_angle_max || (left.second - p).l2_norm_square() > 1.0 / (double)DIVISION_SIZE) {
				q_boundary.emplace(left, make_pair(angle, p));
			}
			if (angle < force_angle_max || (right.second - p).l2_norm_square() > 1.0 / (double)DIVISION_SIZE) {
				q_boundary.emplace(make_pair(angle, p), right);
			}
		}
	}
	return res;
}

// 今いる地点から直進して到達できる箇所を取得する関数
template<bool a_star>
vector<pair<Meter, Vec2<double>>> get_boudary(Vec2<double> pos, const RegionId& region_id, const double& jump_power) {
	bool x_is_integer = is_integer(pos.x);
	bool y_is_integer = is_integer(pos.y);
	vector<pair<Meter, Vec2<double>>> res;
	if (y_is_integer) {
		res = _get_boudary<true, false, a_star>(pos, region_id, jump_power);
	} else if (x_is_integer) {
		res = _get_boudary<false, true, a_star>(pos, region_id, jump_power);
	} else {
		res = _get_boudary<false, false, a_star>(pos, region_id, jump_power);
	}
	return res;
}

// region_ids 等の初期化
void init_region(const Stage& aStage) {
	// >>> 地形の連結成分を調べて region_ids を埋める >>>
	fill_2d_array(region_ids, (RegionId)-1);
	region_terrains.clear();
	RegionId region_id = 0;
	for (int i = 0; i < n_scrolls; i++) {  // 巻物がある場所はそのタイル単体でひとつの region とする
		const Vec2<Meter>& pos = scroll_poses[i];
		region_ids[pos.y][pos.x] = region_id;
		const Terrain& terrain = aStage.terrain(Vector2(pos.x, pos.y));
		region_terrains.push_back(terrain);
		region_id++;
	}
	auto oof = [&](const Vec2<int>& tile) {  // フィールド外かどうか
		return tile.x < 0 || 50 <= tile.x || tile.y < 0 || 50 <= tile.y;
	};
	for (int y = 0; y < 50; y++) {  // 地形が同じ隣接した区画を region としてまとめる
		for (int x = 0; x < 50; x++) {
			if (region_ids[y][x] != -1) continue;
			const Terrain& terrain = aStage.terrain(Vector2((float)x, (float)y));
			region_terrains.push_back(terrain);
			deque<Vec2<int>> q;
			q.emplace_back(x, y);
			region_ids[y][x] = region_id;
			while (!q.empty()) {
				const Vec2<int> v = q.front();
				q.pop_front();
				for (const Vec2<int>& dxy : Dxy) {
					const Vec2<int> u = v + dxy;
					if (!oof(u) && region_ids[u.y][u.x] == -1 && terrain == aStage.terrain(Vector2((float)u.x, (float)u.y))) {
						region_ids[u.y][u.x] = region_id;
						q.push_back(u);
					}
				}
			}
			region_id++;
		}
	}
	// <<< 地形の連結成分を調べて region_ids を埋める <<<

	// >>> region を長方形に分割して area にする >>>
	fill_2d_array(area_ids, (AreaId)-1);
	area_rectangles.clear();
	AreaId area_id = 0;
	// 貪欲に長方形を作る
	for (int y = 0; y < 50; y++) {
		for (int x = 0; x < 50; x++) {
			if (area_ids[y][x] != -1) continue;
			area_ids[y][x] = area_id;
			RegionId reg_id = region_ids[y][x];
			int r = x + 1;
			for (; r < 50; r++) {
				if (region_ids[y][r] != reg_id || area_ids[y][r] != -1) break;
				area_ids[y][r] = area_id;
			}
			int d = y + 1;
			for (; d < 50; d++) {
				for (int ux = x; ux < r; ux++) {
					if (region_ids[d][ux] != reg_id) goto brbr;
				}
				for (int ux = x; ux < r; ux++) {
					area_ids[d][ux] = area_id;
				}
			}
		brbr:
			area_rectangles.emplace_back(d, r, y, x);
			area_id++;
		}
	}
	// <<< region を長方形に分割して area にする <<<
}


int temporarily_removed_scroll = 0;

// 一時的に巻物を取り除いたとして region_ids を修正する
void modify_region(const Vec2<double>& aPos) {
	const Vec2<Meter> pos(aPos);
	const int idx_scrolls = region_ids[pos.y][pos.x];
	if (idx_scrolls >= n_scrolls) return;  // 巻物が無い場所なら何もしない
	temporarily_removed_scroll = idx_scrolls;
	auto oof = [&](const Vec2<int>& tile) {  // フィールド外かどうか
		return tile.x < 0 || 50 <= tile.x || tile.y < 0 || 50 <= tile.y;
	};
	for (const Vec2<int>& dxy : Dxy) {
		const Vec2<int> p = pos + dxy;
		if (!oof(p)
			&& region_ids[p.y][p.x] >= n_scrolls
			&& region_terrains[region_ids[p.y][p.x]] == region_terrains[region_ids[pos.y][pos.x]]) {  // 隣接タイルが巻物でなく、自分と同じ地形なら
			region_ids[pos.y][pos.x] = region_ids[p.y][p.x];
			return;
		}
	}
}
// 修正を元に戻す
void undo_modify_region() {
	const Vec2<Meter>& pos = scroll_poses[temporarily_removed_scroll];
	region_ids[pos.y][pos.x] = temporarily_removed_scroll;
}

// <<< 関数いろいろ <<<


// >>> ダイクストラ法 >>>

constexpr PosId N_INTERMEDIATE = 50 * 50 * DIVISION_SIZE * DIVISION_SIZE;
constexpr PosId N_Y_INT = 50 * 50 * DIVISION_SIZE;
inline PosId pos_id(const Vec2<double>& pos) {
	//HPC_ASSERT(0.0 < pos.x && pos.x < 50.0 && 0.0 < pos.y && pos.y < 50.0);
	if (is_integer(pos.y)) {
		return N_INTERMEDIATE + (int)pos.y * 50 * DIVISION_SIZE + (int)(pos.x * DIVISION_SIZE);
	} else if (is_integer(pos.x)) {
		return N_INTERMEDIATE + N_Y_INT + (int)pos.x * 50 * DIVISION_SIZE + (int)(pos.y * DIVISION_SIZE);
	} else {
		return (PosId)(pos.y * DIVISION_SIZE) * 50 * DIVISION_SIZE + (int)(pos.x * DIVISION_SIZE);
	}
}

inline vector<PosId> pos_ids(const Vec2<Meter>& pos) {
	vector<PosId> res;
	res.reserve(DIVISION_SIZE * DIVISION_SIZE + 4 * DIVISION_SIZE);
	for (int y = 0; y < DIVISION_SIZE; y++) {
		for (int x = 0; x < DIVISION_SIZE; x++) {
			res.push_back((pos.y * DIVISION_SIZE + y) * 50 * DIVISION_SIZE + pos.x * DIVISION_SIZE + x);
		}
	}
	for (int i = 0; i < DIVISION_SIZE; i++) {
		if (pos.y != 0) res.push_back(N_INTERMEDIATE + pos.y * 50 * DIVISION_SIZE + pos.x * DIVISION_SIZE + i);
		if (pos.y != 49) res.push_back(N_INTERMEDIATE + (pos.y + 1) * 50 * DIVISION_SIZE + pos.x * DIVISION_SIZE + i);
		if (pos.x != 0) res.push_back(N_INTERMEDIATE + N_Y_INT + pos.x * 50 * DIVISION_SIZE + pos.y * DIVISION_SIZE + i);
		if (pos.x != 49) res.push_back(N_INTERMEDIATE + N_Y_INT + (pos.x + 1) * 50 * DIVISION_SIZE + pos.y * DIVISION_SIZE + i);
	}
	return res;
}

array<Meter, 50 * 50 * DIVISION_SIZE*(2 + DIVISION_SIZE)> dijkstra_dists;
array<Vec2<double>, 50 * 50 * DIVISION_SIZE*(2 + DIVISION_SIZE)> dijkstra_poses;
array<PosId, 50 * 50 * DIVISION_SIZE*(2 + DIVISION_SIZE)> dijkstra_froms;
array<array<bool, 50>, 50> dijkstra_targets;

// 今の地点に未取得の巻物が存在するか確認する関数
// 存在すれば取得して取得枚数を返す
inline int check_scroll(const PosId& id) {
	if (id < N_INTERMEDIATE) {
		const int y = id / (50 * DIVISION_SIZE * DIVISION_SIZE);
		const int x = (id % (50 * DIVISION_SIZE)) / DIVISION_SIZE;
		if (dijkstra_targets[y][x]) {
			dijkstra_targets[y][x] = false;
			return 1;
		} else {
			return 0;
		}
	} else if (id < N_INTERMEDIATE + N_Y_INT) {
		const int y = (id - N_INTERMEDIATE) / (50 * DIVISION_SIZE);
		const int x = ((id - N_INTERMEDIATE) % (50 * DIVISION_SIZE)) / DIVISION_SIZE;
		int res = 0;
		if (dijkstra_targets[y][x]) {
			dijkstra_targets[y][x] = false;
			res++;
		}
		if (dijkstra_targets[y - 1][x]) {
			dijkstra_targets[y - 1][x] = false;
			res++;
		}
		return res;
	} else {
		const int x = (id - N_INTERMEDIATE - N_Y_INT) / (50 * DIVISION_SIZE);
		const int y = ((id - N_INTERMEDIATE - N_Y_INT) % (50 * DIVISION_SIZE)) / DIVISION_SIZE;
		int res = 0;
		if (dijkstra_targets[y][x]) {
			dijkstra_targets[y][x] = false;
			res++;
		}
		if (dijkstra_targets[y][x - 1]) {
			dijkstra_targets[y][x - 1] = false;
			res++;
		}
		return res;
	}
}

// ダイクストラ法
void dijkstra(const Vec2<double>& start, const double& power, const int& n_targets) {
	long long q_pop_cnt = 0, u_cnt = 0;
	struct QElem {
		Meter dist;
		PosId id;
		QElem(Meter aDist, int aId) : dist(aDist), id(aId) {}
		bool operator<(const QElem& rhs)const {
			return dist < rhs.dist;
		}
		bool operator>(const QElem& rhs)const {
			return dist > rhs.dist;
		}
	};
	auto get_region_id = [&](const Vec2<double>& p) {
		if (is_integer(p.y)) {
			const RegionId &id1 = region_ids[(int)p.y][(int)p.x], &id2 = region_ids[(int)p.y - 1][(int)p.x];
			return (int)region_terrains[id1] > (int)region_terrains[id2] ? id1 : id2;
		} else if (is_integer(p.x)) {
			const RegionId &id1 = region_ids[(int)p.y][(int)p.x], &id2 = region_ids[(int)p.y][(int)p.x - 1];
			return (int)region_terrains[id1] > (int)region_terrains[id2] ? id1 : id2;
		} else {
			return region_ids[(int)p.y][(int)p.x];
		}
	};

	modify_region(start);  // region_ids の修正
	fill(dijkstra_dists.begin(), dijkstra_dists.end(), (Meter)10000);
	const PosId start_id = pos_id(start);
	dijkstra_dists[start_id] = (Meter)0;
	dijkstra_poses[start_id] = start;
	int n_remain_targets = min(10, n_targets);  // 枝刈り
	priority_queue<QElem, vector<QElem>, greater<QElem>> q;
	q.emplace(0, start_id);
	while (!q.empty()) {
		const QElem v = q.top();
		q.pop();
		q_pop_cnt++;
		if (dijkstra_dists[v.id] != v.dist) continue;
		n_remain_targets -= check_scroll(v.id);
		if (n_remain_targets == 0) break;
		Vec2<double> v_pos = dijkstra_poses[v.id];
		auto us = get_boudary<false>(v_pos, get_region_id(v_pos), power);
		for (const auto& u : us) {
			u_cnt++;
			const PosId u_id = pos_id(u.second);
			//HPC_ASSERT(u.first >= 1);
			//HPC_ASSERT((v_pos - u.second).l2_norm() < u.first * power * 1.01);
			const Meter new_dist = v.dist + u.first;
			if (dijkstra_dists[u_id] > new_dist) {
				dijkstra_dists[u_id] = new_dist;
				dijkstra_poses[u_id] = u.second;
				dijkstra_froms[u_id] = v.id;
				q.emplace(new_dist, u_id);
			}
		}
	}
	undo_modify_region();
}

// ダイクストラの後処理
void dijkstra_postprocess(const int& start_scroll) {  // start_scroll が -1 のとき、初期地点スタートのダイクストラの処理 そうでなければ巻物スタート
	for (int goal_scroll = 0; goal_scroll < n_scrolls; goal_scroll++) {
		const Vec2<Meter>& goal_pos = scroll_poses[goal_scroll];
		const vector<PosId> goal_ids = pos_ids(goal_pos);
		Meter& d = start_scroll == -1 ? scroll_distance_from_start[goal_scroll] : scroll_distance[start_scroll][goal_scroll];
		d = 10000;
		for (const PosId& goal_id : goal_ids) {
			if (d > dijkstra_dists[goal_id]) {
				d = dijkstra_dists[goal_id];
			}
		}
	}
}

// <<< ダイクストラ法 <<<


// >>> 巡回セールスマン問題 >>>

inline int popcount(unsigned int n) {
	n = (n & 0x55555555) + ((n >> 1) & 0x55555555);
	n = (n & 0x33333333) + ((n >> 2) & 0x33333333);
	n = (n & 0x0f0f0f0f) + ((n >> 4) & 0x0f0f0f0f);
	n = (n & 0x00ff00ff) + ((n >> 8) & 0x00ff00ff);
	n = (n & 0x0000ffff) + ((n >> 16) & 0x0000ffff);
	return n;
}

array<array<int, 20>, 1 << 20> tsp_dp;  // 実際の 10000 倍して持つ
array<array<char, 20>, 1 << 20> tsp_from;
array<array<array<int, 20>, 20>, 20> tsp_dist;
vector<char> tsp_path;
double expected_score = 0.0;

// 巡回セールスマン問題を動的計画法で解く関数
template <int _n_scrolls> void tsp() {
	for (int i = 0; i < _n_scrolls; i++) {
		for (int j = 0; j < _n_scrolls; j++) {
			double d = (double)scroll_distance[i][j];
			//HPC_ASSERT(d <= 1e5);
			for (int n_gotten = 0; n_gotten < _n_scrolls; n_gotten++) {
				tsp_dist[n_gotten][i][j] = (int)(d * 10000.0);
				d /= 1.1;
			}
		}
	}

	for (int j = 0; j < _n_scrolls; j++) {
		tsp_dp[1 << j][j] = (int)scroll_distance_from_start[j] * 10000;
	}
	for (int s = 1; s < (1 << _n_scrolls) - 1; s++) {
		const int n_gotten = popcount(s);
		for (char i = 0; i < (char)_n_scrolls; i++) {
			if ((s >> i & 1) == 0) continue;
#define TSP_PROCESS(j) if (j < _n_scrolls && (s >> j & 1) == 0) { \
                            int d = tsp_dp[s][i] + tsp_dist[n_gotten][i][j]; \
                            if (tsp_dp[s | 1 << j][j] > d) { \
                                tsp_dp[s | 1 << j][j] = d; \
                                tsp_from[s | 1 << j][j] = i; \
                            } \
                        }
			TSP_PROCESS(0);
			TSP_PROCESS(1);
			TSP_PROCESS(2);
			TSP_PROCESS(3);
			TSP_PROCESS(4);
			TSP_PROCESS(5);
			TSP_PROCESS(6);
			TSP_PROCESS(7);
			TSP_PROCESS(8);
			TSP_PROCESS(9);
			TSP_PROCESS(10);
			TSP_PROCESS(11);
			TSP_PROCESS(12);
			TSP_PROCESS(13);
			TSP_PROCESS(14);
			TSP_PROCESS(15);
			TSP_PROCESS(16);
			TSP_PROCESS(17);
			TSP_PROCESS(18);
			TSP_PROCESS(19);
#undef TSP_PROCESS
			// ↑は↓をループアンローリングしたもの
			/*
			for (int j = 0; j < _n_scrolls; j++) {
				if (s >> j & 1) continue;
				int d = tsp_dp[s][i] + tsp_dist[n_gotten][i][j];
				if (tsp_dp[s | 1 << j][j] > d) {
					tsp_dp[s | 1 << j][j] = d;
					tsp_from[s | 1 << j][j] = i;
				}
			}
			*/
			tsp_dp[s][i] = (int)1e9;
		}
	}
	int s = (1 << _n_scrolls) - 1;
	const int min_idx = min_element(tsp_dp[s].data(), tsp_dp[s].data() + _n_scrolls) - tsp_dp[s].data();
	const int d = tsp_dp[s][min_idx];
	for (int i = 0; i < _n_scrolls; i++) {
		tsp_dp[s][i] = (int)1e9;
	}

	tsp_path.resize(_n_scrolls);
	int p = min_idx;
	for (int i = _n_scrolls - 1; i >= 0; i--) {
		tsp_path[i] = p;
		const int next_s = s ^ 1 << p;
		p = tsp_from[s][p];
		s = next_s;
	}
#ifdef PRINT_DEBUG
	cerr << "tsp dist = " << (double)d / 10000.0 << endl;
	for (const int pa : tsp_path) {
		cerr << pa << " ";
	}
	cerr << endl;
#endif
	expected_score += (double)d / 10000.0;
}

// <<< 巡回セールスマン問題 <<<


// >>> A* 探索 >>>

array<Meter, 50 * 50 * A_STAR_DIVISION_SIZE*(2 + A_STAR_DIVISION_SIZE) + A_STAR_N_STARTS * 2> a_star_dists;
array<Vec2<double>, 50 * 50 * A_STAR_DIVISION_SIZE*(2 + A_STAR_DIVISION_SIZE) + A_STAR_N_STARTS * 2> a_star_poses;
array<AStarPosId, 50 * 50 * A_STAR_DIVISION_SIZE*(2 + A_STAR_DIVISION_SIZE) + A_STAR_N_STARTS * 2> a_star_froms;
inline vector<AStarPosId> a_star_pos_ids(const Vec2<Meter>& pos) {
	vector<AStarPosId> res;
	res.reserve(A_STAR_DIVISION_SIZE * A_STAR_DIVISION_SIZE + 4 * A_STAR_DIVISION_SIZE);
	for (int y = 0; y < A_STAR_DIVISION_SIZE; y++) {
		for (int x = 0; x < A_STAR_DIVISION_SIZE; x++) {
			res.push_back((pos.y * A_STAR_DIVISION_SIZE + y) * 50 * A_STAR_DIVISION_SIZE + pos.x * A_STAR_DIVISION_SIZE + x);
		}
	}
	for (int i = 0; i < A_STAR_DIVISION_SIZE; i++) {
		if (pos.y != 0) res.push_back(A_STAR_N_INTERMEDIATE + pos.y * 50 * A_STAR_DIVISION_SIZE + pos.x * A_STAR_DIVISION_SIZE + i);
		if (pos.y != 49) res.push_back(A_STAR_N_INTERMEDIATE + (pos.y + 1) * 50 * A_STAR_DIVISION_SIZE + pos.x * A_STAR_DIVISION_SIZE + i);
		if (pos.x != 0) res.push_back(A_STAR_N_INTERMEDIATE + A_STAR_N_Y_INT + pos.x * 50 * A_STAR_DIVISION_SIZE + pos.y * A_STAR_DIVISION_SIZE + i);
		if (pos.x != 49) res.push_back(A_STAR_N_INTERMEDIATE + A_STAR_N_Y_INT + (pos.x + 1) * 50 * A_STAR_DIVISION_SIZE + pos.y * A_STAR_DIVISION_SIZE + i);
	}
	return res;
}
// A* 探索 (多点スタート)
void a_star(const vector<pair<Meter, Vec2<double>>>& starts, const Vec2<Meter>& int_start, const Vec2<Meter>& goal, const double& power) {
	struct QElem {
		Meter g;
		Meter dist;
		AStarPosId id;
		QElem(const Meter& aG, const Meter& aDist, const int& aId) : g(aG), dist(aDist), id(aId) {}
		bool operator<(const QElem& rhs)const {
			return g < rhs.g;
		}
		bool operator>(const QElem& rhs)const {
			return g > rhs.g;
		}
	};
	auto get_region_id = [&](const Vec2<double>& p) {
		if (is_integer(p.y)) {
			const RegionId &id1 = region_ids[(int)p.y][(int)p.x], &id2 = region_ids[(int)p.y - 1][(int)p.x];
			return (int)region_terrains[id1] > (int)region_terrains[id2] ? id1 : id2;
		} else if (is_integer(p.x)) {
			const RegionId &id1 = region_ids[(int)p.y][(int)p.x], &id2 = region_ids[(int)p.y][(int)p.x - 1];
			return (int)region_terrains[id1] > (int)region_terrains[id2] ? id1 : id2;
		} else {
			return region_ids[(int)p.y][(int)p.x];
		}
	};
	auto is_goal = [&](const Vec2<double>& p) {
		return goal.x <= p.x && p.x <= goal.x + 1 && goal.y <= p.y && p.y <= goal.y + 1;
	};
	auto a_star_pos_id = [&](const Vec2<double>& pos) -> AStarPosId {
		//HPC_ASSERT(0.0 < pos.x && pos.x < 50.0 && 0.0 < pos.y && pos.y < 50.0);
		if (is_integer(pos.y)) {
			return A_STAR_N_INTERMEDIATE + (int)pos.y * 50 * A_STAR_DIVISION_SIZE + (int)(pos.x * A_STAR_DIVISION_SIZE);
		} else if (is_integer(pos.x)) {
			return A_STAR_N_INTERMEDIATE + A_STAR_N_Y_INT + (int)pos.x * 50 * A_STAR_DIVISION_SIZE + (int)(pos.y * A_STAR_DIVISION_SIZE);
		} else {
			return (AStarPosId)(pos.y * A_STAR_DIVISION_SIZE) * 50 * A_STAR_DIVISION_SIZE + (int)(pos.x * A_STAR_DIVISION_SIZE);
		}
	};

	auto get_special_us = [&](const Vec2<double>& pos, const RegionId& reg_id) {  // 角に飛ぶ
		Terrain terrain = region_terrains[reg_id];
		const double jump_dist = TERRAIN_COEF[(int)terrain] * power;
		vector<pair<Meter, Vec2<double>>> res;
		for (const double& fy : array<double, 2>{2e-3, 1.0 - 2e-3}) for (const double& fx : array<double, 2>{2e-3, 1.0 - 2e-3}) {
			const Vec2<double> g = Vec2<double>(goal) + Vec2<double>(fx, fy);
			//HPC_ASSERT(0.0 < g.x && g.x < 50.0 && 0.0 < g.y && g.y < 50.0);
			const Vec2<double> vec = g - pos;
			const double dist = vec.l2_norm();
			const Vec2<double> jump = vec * (jump_dist / dist);
			const int n = (int)(dist / jump_dist);
			if (n >= 20) continue;
			Vec2<double> p(pos);
			for (int i = 1; i <= n; i++) {
				p += jump;
				Vec2<int> int_p(p);
				if (region_terrains[region_ids[int_p.y][int_p.x]] != terrain) goto brcon;
			}
			res.emplace_back(n + 1, g);
		brcon:;
		}
		return res;
	};
	constexpr double inv_root_2 = 0.70710678118655;

	modify_region(Vec2<double>(int_start));  // region 修正
	fill(a_star_dists.begin(), a_star_dists.end(), (Meter)10000);  // 距離の初期化
	priority_queue<QElem, vector<QElem>, greater<QElem>> q;
	for (int idx_starts = 0; idx_starts < (int)starts.size(); idx_starts++) {  // プライオリティキューの初期化
		const pair<Meter, Vec2<double>>& s = starts[idx_starts];
		const Meter& dist = s.first;
		Vec2<double> p = s.second;
		const AStarPosId id = A_STAR_N_NORMAL + idx_starts;
		const Meter h = (Meter)ceil(
			(
				(p - ((Vec2<double>)(goal) + 0.5)).l2_norm() - inv_root_2
			) / power
		);
		a_star_dists[id] = dist;
		a_star_poses[id] = p;

		q.emplace(dist + h, dist, id);
	}

	const vector<AStarPosId> goals = a_star_pos_ids(goal);
	int n_remain_goals = (int)goals.size();
	int n_a_star_nodes = 0;
	int best_dist = 10000;
	while (!q.empty()) {
		const QElem v = q.top();
		q.pop();
		if (a_star_dists[v.id] != v.dist) continue;
		n_a_star_nodes++;
		const Vec2<double>& v_pos = a_star_poses[v.id];

		// 終了判定
		if (is_goal(v_pos)) {
			n_remain_goals--;
			if (best_dist == 10000) best_dist = v.dist;
			if (n_remain_goals == 0) break;
		}
		if (best_dist + 2 < v.dist) break;


		const auto special_us = get_special_us(v_pos, get_region_id(v_pos));
		for (const auto& u : special_us) {
			const AStarPosId u_id = a_star_pos_id(u.second);
			const Meter new_dist = v.dist + u.first;
			if (a_star_dists[u_id] > new_dist) {
				a_star_dists[u_id] = new_dist;
				a_star_poses[u_id] = u.second;
				a_star_froms[u_id] = v.id;
				const Meter h = (Meter)ceil(
					(
						(u.second - ((Vec2<double>)(goal) + 0.5)).l2_norm() - inv_root_2
					) / power
				);
				q.emplace(new_dist + h, new_dist, u_id);
			}
		}


		const auto us = get_boudary<true>(v_pos, get_region_id(v_pos), power);
		for (const auto& u : us) {
			const AStarPosId u_id = a_star_pos_id(u.second);
			const Meter new_dist = v.dist + u.first;
			if (a_star_dists[u_id] > new_dist) {
				a_star_dists[u_id] = new_dist;
				a_star_poses[u_id] = u.second;
				a_star_froms[u_id] = v.id;
				const Meter h = (Meter)ceil(
					(
						(u.second - ((Vec2<double>)(goal) + 0.5)).l2_norm() - inv_root_2
					) / power
				);
				q.emplace(new_dist + h, new_dist, u_id);
			}
		}
	}
#ifdef PRINT_DEBUG
	cerr << "n_a_star_nodes=" << n_a_star_nodes << " n_remain_goals=" << n_remain_goals << endl;
#endif
	//HPC_ASSERT(n_remain_goals < (int)goals.size());
	undo_modify_region();  // region の修正を戻す
}

// <<< A* 探索 <<<


vector<Vec2<double>> overall_path;
int calculated_score = 0;
int calculated_scr;
int real_score;
void find_answer(const Vec2<double>& start) {
	// 探索
	vector<pair<Meter, Vec2<double>>> starts{ make_pair(0, start) };
	Vec2<Meter> int_start(start);
	vector<vector<vector<Vec2<double>>>> pathss;
	vector<vector<pair<Meter, Vec2<double>>>> startss;
	double power = 1.0;
	for (int idx_scrolls = 0; idx_scrolls < n_scrolls; idx_scrolls++) {
		startss.push_back(starts);
		const Vec2<Meter>& goal = scroll_poses[tsp_path[idx_scrolls]];

		a_star(starts, int_start, goal, power);

		const vector<AStarPosId> goals = a_star_pos_ids(goal);
		starts.clear();

		// starts の更新
		for (const AStarPosId& id : goals) {
			Vec2<double>& p = a_star_poses[id];
			if (is_integer(p.y)) {  // 内側へ寄せる
				if ((Meter)p.y == goal.y) p.y += EPS;
				else p.y -= EPS;
			}
			if (is_integer(p.x)) {
				if ((Meter)p.x == goal.x) p.x += EPS;
				else p.x -= EPS;
			}
			starts.emplace_back(a_star_dists[id], p);
		}
		// 道筋の記録
		vector<vector<Vec2<double>>> paths;
		for (AStarPosId id : goals) {
			vector<Vec2<double>> path;
			if (a_star_dists[id] == 10000) {  // 未到達
				path.emplace_back(-1.0, -1.0);  // ダミー
			} else {
				while (id < A_STAR_N_NORMAL) {
					id = a_star_froms[id];
					path.push_back(a_star_poses[id]);
				}
			}
			reverse(path.begin(), path.end());
			paths.push_back(path);
		}
		pathss.push_back(paths);

		int_start = goal;
		power *= 1.1;
	}

	// 復元
	overall_path.clear();
	Meter d = 10000;
	int argmin_starts = -1;
	for (int i = 0; i < (int)starts.size(); i++) {
		if (starts[i].first < d) {
			d = starts[i].first;
			argmin_starts = i;
		}
	}
	//HPC_ASSERT(-1 != argmin_starts);

	calculated_score += d;
	calculated_scr = d;
	overall_path.push_back(starts[argmin_starts].second);
	for (int idx_pathss = n_scrolls - 1; idx_pathss >= 0; idx_pathss--) {
		const vector<Vec2<double>>& path = pathss[idx_pathss][argmin_starts];
		for (int idx_path = (int)path.size() - 1; idx_path >= 0; idx_path--) {
			overall_path.push_back(path[idx_path]);  // 逆順
		}
		starts = startss[idx_pathss];
		argmin_starts = -1;
		for (int i = 0; i < (int)starts.size(); i++) {
			if (starts[i].second == path[0]) {
				argmin_starts = i;
			}
		}
		//HPC_ASSERT(-1 != argmin_starts);
	}
	reverse(overall_path.begin(), overall_path.end());
}

// 浮動小数点の誤差を減らすように座標を修正 本当に誤差を減らせているかはよくわからない
void fix_path() {
	for (Vec2<double>& p : overall_path) {
		Vec2<int> int_p(p);
		Vec2<double> frac_p = p - Vec2<double>(int_p);
		if (eq(frac_p.x, 0.0)) {
			if ((int)region_terrains[region_ids[int_p.y][int_p.x]] < (int)region_terrains[region_ids[int_p.y][int_p.x - 1]]) {
				p.x += 5e-5;
			} else {
				p.x -= 5e-5;
			}
		} else if (frac_p.x < 4e-5) {
			p.x += 4e-5;
		}
		if (frac_p.x > 1 - 4e-5) {
			p.x -= 4e-5;
		}
		if (eq(frac_p.y, 0.0)) {
			if ((int)region_terrains[region_ids[int_p.y][int_p.x]] < (int)region_terrains[region_ids[int_p.y - 1][int_p.x]]) {
				p.y += 5e-5;
			} else {
				p.y -= 5e-5;
			}
		} else if (frac_p.y < 4e-5) {
			p.y += 4e-5;
		} else if (frac_p.y > 1 - 4e-5) {
			p.y -= 4e-5;
		}
	}
}

//------------------------------------------------------------------------------
/// コンストラクタ
/// @detail 最初のステージ開始前に実行したい処理があればここに書きます
Answer::Answer() {
	init_sin_cos_table();
	T0 = time();
	stage_num = 0;
	fill_2d_array(tsp_dp, (int)1e9);
}

//------------------------------------------------------------------------------
/// デストラクタ
/// @detail 最後のステージ終了後に実行したい処理があればここに書きます
Answer::~Answer() {
#ifdef PRINT_DEBUG
	cerr << "expected_score=" << expected_score << endl;
	cerr << "calculated_score=" << calculated_score << endl;
	cerr << "real_score=" << real_score << endl;
	int a;
	cin >> a;
#endif
}

// >>> テスト >>>

void test_go_straight() {
	for (Angle angle = 0; angle < 8; angle++) {
		const Vec2<double>& p = go_straight(Vec2<double>(0.5, 0.5), angle, n_scrolls);
		cerr << p << endl;
	}
	cerr << endl;
	for (Angle angle = 0; angle < 8; angle++) {
		const Vec2<double>& p = go_straight(Vec2<double>(49.5, 49.5), angle, region_ids[49][49]);
		cerr << p << endl;
	}
}

void test_get_boudary() {
	//auto bs = get_boudary(Vec2<double>(0.5, 0.5), region_ids[0][0], 1.0);
	auto bs = get_boudary<false>(Vec2<double>(14.0, 0.5), region_ids[0][14], 1.0);
	for (auto& b : bs) {
		cerr << b.first << " " << b.second << endl;
	}
}

void test_dijkstra() {
	cerr << "dijkstra start!" << endl;
	double t0 = time();
	dijkstra(Vec2<double>(0.5, 0.5), 1.0, 999);
	for (const auto& dist : dijkstra_dists) {
		cerr << dist << " ";
	}
	cerr << endl;
	dijkstra(Vec2<double>(20.5, 0.5), 1.0, 999);
	dijkstra(Vec2<double>(20.5, 20.5), 1.0, 999);
	dijkstra(Vec2<double>(20.5, 30.5), 1.0, 999);
	dijkstra(Vec2<double>(20.5, 40.5), 1.0, 999);
	dijkstra(Vec2<double>(10.5, 40.5), 1.0, 999);
	dijkstra(Vec2<double>(30.5, 40.5), 1.0, 999);
	dijkstra(Vec2<double>(40.5, 40.5), 1.0, 999);
	double t = time() - t0;
	cerr << "dijkstra finished!" << endl;
	cerr << "time: " << t << " sec" << endl;
}

// <<< テスト <<<


int next_scroll_idx;
int next_path;

//------------------------------------------------------------------------------
/// 各ステージ開始時に呼び出される処理
/// @detail 各ステージに対する初期化処理が必要ならここに書きます
/// @param aStage 現在のステージ
void Answer::initialize(const Stage& aStage) {
	next_scroll_idx = 0;
	next_path = 0;
	const auto& scrolls = aStage.scrolls();
	n_scrolls = scrolls.count();
	for (int i = 0; i < scrolls.count(); i++) {
		const auto& pos = scrolls[i].pos();
		scroll_poses[i] = Vec2<Meter>((Meter)pos.x, (Meter)pos.y);
	}
	init_region(aStage);

	fill_2d_array(dijkstra_targets, false);
	double dijkstra_power = pow(1.1, (n_scrolls - 1.0) * 0.5);
	for (int idx_scrolls = 0; idx_scrolls < n_scrolls; idx_scrolls++) {
		const Vec2<Meter>& scroll = scroll_poses[idx_scrolls];
		for (int i = 0; i < n_scrolls; i++) {
			const Vec2<Meter>& p = scroll_poses[i];
			if (idx_scrolls != i) dijkstra_targets[p.y][p.x] = true;
		}
		const Vec2<double> start((double)scroll.x + 0.5, (double)scroll.y + 0.5);
		dijkstra(start, dijkstra_power, n_scrolls - 1);
		dijkstra_postprocess(idx_scrolls);
	}
	for (int i = 0; i < n_scrolls; i++) {
		const Vec2<Meter>& p = scroll_poses[i];
		dijkstra_targets[p.y][p.x] = true;
	}
	dijkstra(aStage.rabbit().pos(), dijkstra_power, n_scrolls);
	dijkstra_postprocess(-1);

	if (n_scrolls == 1) tsp<1>();
	else if (n_scrolls == 2) tsp<2>();
	else if (n_scrolls == 3) tsp<3>();
	else if (n_scrolls == 4) tsp<4>();
	else if (n_scrolls == 5) tsp<5>();
	else if (n_scrolls == 6) tsp<6>();
	else if (n_scrolls == 7) tsp<7>();
	else if (n_scrolls == 8) tsp<8>();
	else if (n_scrolls == 9) tsp<9>();
	else if (n_scrolls == 10) tsp<10>();
	else if (n_scrolls == 11) tsp<11>();
	else if (n_scrolls == 12) tsp<12>();
	else if (n_scrolls == 13) tsp<13>();
	else if (n_scrolls == 14) tsp<14>();
	else if (n_scrolls == 15) tsp<15>();
	else if (n_scrolls == 16) tsp<16>();
	else if (n_scrolls == 17) tsp<17>();
	else if (n_scrolls == 18) tsp<18>();
	else if (n_scrolls == 19) tsp<19>();
	else if (n_scrolls == 20) tsp<20>();
	//else HPC_ASSERT(false);

	find_answer(aStage.rabbit().pos());
	fix_path();

#ifdef PRINT_DEBUG
	cerr << "stage " << stage_num << ": " << time() - T0 << " sec" << endl;
#endif
	stage_num++;
}


//------------------------------------------------------------------------------
/// 毎フレーム呼び出される処理
/// @detail 移動先を決定して返します
/// @param aStage 現在のステージ
/// @return 移動の目標座標
Vector2 Answer::getTargetPos(const Stage& aStage) {
	auto pos = aStage.rabbit().pos();
	while (next_path < (int)overall_path.size() && (Vec2<double>(pos) - overall_path[next_path]).l2_norm() < 3e-4) {
		next_path++;
	}
	if (next_path < (int)overall_path.size()) {
		Vec2<double> p = overall_path[next_path];
		return (Vector2)p;
	}
	return pos;
}

//------------------------------------------------------------------------------
/// 各ステージ終了時に呼び出される処理
/// @detail 各ステージに対する終了処理が必要ならここに書きます
/// @param aStage 現在のステージ
void Answer::finalize(const Stage& aStage) {
	real_score += aStage.turn();
#ifdef PRINT_DEBUG
	cerr << "calculated_score=" << calculated_scr << " actural_score=" << aStage.turn() << endl << endl;
#endif
}

} // namespace

#undef PRINT_DEBUG

// EOF