// Web 0.5 #include #include #include #include #include #include #include #include using namespace std; #define for if(0);else for #ifndef M_PI #define M_PI 3.14159265358979323846 #endif//M_PI /**************************************************************************** * データ型定義 ****************************************************************************/ /** 座標をあらわす */ typedef pair point; /** 巣の壊れている場所を示すデータ構造 */ struct broken { enum type_enum { RAD = 0, ARG = 1, } type; int rad, idx; broken(type_enum type, int rad, int idx) : type(type), rad(rad), idx(idx) { } }; /** * 座標を入力する。 * * @param in 入力ストリーム * @param p 入力先 * @return 座標を読んだ後の入力ストリーム */ istream& operator >> (istream& in, point& p) { return in >> p.first >> p.second; } /**************************************************************************** * Dijkstra ****************************************************************************/ /** 辺 = {コスト, 行先} */ typedef pair edge; /** 辺集合 */ typedef vector edges; /** グラフの型 */ typedef map graph; /** * Dijkstra のアルゴリズムを実装する。 * * @param G グラフ * @param src 出発地 * @param dst 目的地 * @return src から dst までの最短距離。到達不能な場合負の値が返る。 */ double dijkstra(graph& G, point src, point dst) { map len; priority_queue, greater > Q; Q.push(edge(0, src)); while(! Q.empty()) { edge c = Q.top(); Q.pop(); #ifdef _DEBUG cerr << "c=(" << c.second.first << "," << c.second.second << ")@" << c.first << endl; #endif//_DEBUG if(len.count(c.second)) continue; // 既に探索終了 if(c.second == dst) return c.first; // 目的地に到達 len[c.second] = c.first; const edges& ne = G[c.second]; for(edges::const_iterator i = ne.begin(); i != ne.end(); ++i) if(! len.count(i->second)) Q.push(edge(c.first + i->first, i->second)); } return -1.0; // 到達不可能である } /**************************************************************************** * メイン ****************************************************************************/ /** * 問題を解く * * @param prob 巣の欠損 * @param src 開始地点 * @param tgt 終了地点 */ double solve(int N, vector& prob, const point& src, const point& tgt) { const double rad_dist = 1.0; const double arg_dist = 2.0 * cos(M_PI * (N - 2) / N / 2.0); #ifdef _DEBUG cerr << "N=" << N << ", dist=" << arg_dist << endl; #endif//_DEBUG // 故障地点を分類しなおす map > broken_rad, broken_arg; for(int i = 0; i < prob.size(); i++) switch(prob[i].type) { case broken::RAD: // (i, j) - (i+1, j) // --+---+---+-- i + 1 // | * | // | * | // --+---+---+-- i broken_rad[prob[i].rad].insert(prob[i].idx); break; case broken::ARG: // (i, j) - (i, j % N + 1) // j j%N+1 // +---+ i + 1 // | | // +***+ i // | | // +---+ i - 1 broken_arg[prob[i].rad].insert(prob[i].idx); break; default: assert(false); } // グラフをつくる必要のある点の集合(半径のみ)を求める set required_pts; for(map >::const_iterator it = broken_rad.begin(), eit = broken_rad.end(); it != eit; ++it) { required_pts.insert(it->first); required_pts.insert(it->first + 1); } for(map >::const_iterator it = broken_arg.begin(), eit = broken_arg.end(); it != eit; ++it) { if(it->first > 1) required_pts.insert(it->first - 1); required_pts.insert(it->first); required_pts.insert(it->first + 1); } required_pts.insert(src.first); required_pts.insert(tgt.first); required_pts.insert(1); // 最内周まで戻ったほうが近い場合がある // グラフを作る graph G; set::const_iterator it = required_pts.begin(); set::const_iterator oit = it++; while(it != required_pts.end()) { const int r = *oit; // 半径 oit のところをぐるっと一周 for(int i = 1; i <= N; i++) { if(broken_arg.count(r) && broken_arg[r].count(i)) continue; G[point(r, i)].push_back(make_pair(r * arg_dist, point(r, i % N + 1))); G[point(r, i % N + 1)].push_back(make_pair(r * arg_dist, point(r, i))); } // oit と it の間の直径方向の糸 /*[要証明?] * ・この間の移動に角度方向の糸は使用されない。 *  なるべく内周で移動した方が距離が短くて済むから。 */ for(int i = 1; i <= N; i++) { if(broken_rad.count(r) && broken_rad[r].count(i)) continue; G[point(r, i)].push_back(make_pair(*it - r, point(*it, i))); G[point(*it, i)].push_back(make_pair(*it - r, point(r, i))); } // 次 oit = it++; } // 半径 oit のところをぐるっと一周 { const int r = *oit; for(int i = 1; i <= N; i++) { // broken_arg のところは必要ない。はず。 if(broken_arg.count(r) && broken_arg[r].count(i)) assert(false); G[point(r, i)].push_back(make_pair(r * arg_dist, point(r, i % N + 1))); G[point(r, i % N + 1)].push_back(make_pair(r * arg_dist, point(r, i))); } } // あとは一発 return dijkstra(G, src, tgt); } /** * メインルーチン */ int main() { int N, X; while(cin >> N >> X, N || X) { // 現在地、目的地 point src, tgt; cin >> src >> tgt; vector prob; int idx = 1; while(X-- > 0) { point a, b; cin >> a >> b; if(b < a) swap(a, b); // adjacency check if(a.first == b.first) { // 同じ半径 ⇒ 角度方向 if(a.second == 1 && b.second == N) swap(a, b); else assert(a.second + 1 == b.second); prob.push_back(broken(broken::ARG, a.first, a.second)); } else if(a.second == b.second) { // 同じ角度 ⇒ 半径方向 assert(a.first + 1 == b.first); prob.push_back(broken(broken::RAD, a.first, a.second)); } else { // 問題ミス assert(false); } } // 計算する static char buffer[4097]; snprintf(buffer, sizeof(buffer) - 1, "%.6lf", solve(N, prob, src, tgt)); cout << buffer << endl; } }