#include #include #include using namespace std; using namespace atcoder; #define print(n) cout << (n) << endl #define fprint(n) cout << setprecision(16) << (n) << endl #define ceil_div(a, b) (((a) - 1) / (b) + 1) #define rep(i, l, n) for (int i = (l); i < (n); i++) #define itrep(itr, st) for (auto itr = st.begin(); itr != st.end(); itr++) #define ite(i, a) for (auto& i : a) #define all(x) x.begin(), x.end() #define lb lower_bound #define ub upper_bound #define lbi(A, x) (ll)(lb(all(A), x) - A.begin()) #define ubi(A, x) (ll)(ub(all(A), x) - A.begin()) using str = string; using ll = long long; using Pair = pair; using mint = modint1000000007; using Mint = modint998244353; template using V = vector; template using VV = V >; template using PQ = priority_queue, greater >; template using PQR = priority_queue; template using BIT = fenwick_tree; const ll INF = 4611686018427387903; const int inf = 2147483647; const ll mod = 1000000007; const ll MOD = 998244353; template inline V getList(int n) { V res(n); rep(i, 0, n) { cin >> res[i]; }return res; } template inline VV getGrid(int m, int n) { VV res(m, V(n)); rep(i, 0, m) { res[i] = getList(n); }return res; } template inline void prints(V& vec) { if (vec.size() == 0)return; cout << vec[0]; rep(i, 1, vec.size()) { cout << ' ' << vec[i]; } cout << '\n'; } inline V dtois(string& s) { V vec = {}; ite(e, s) { vec.push_back(e - '0'); } return vec; } inline V atois(string& s) { V vec = {}; ite(e, s) { vec.push_back(e - 'a'); } return vec; } inline V Atois(string& s) { V vec = {}; ite(e, s) { vec.push_back(e - 'A'); } return vec; } #define TIME 900 //グローバル変数 int N, M, C; struct Point; V points; V > xbt; V > ybt; V > nxbt; V > nybt; V > lxbt; V > lybt; V > lnxbt; V > lnybt; V > ans = {}; //答えとなる点の集合 //2乗 inline int squ(int x) { return x * x; } //二次元平面上の点 struct Point { int x; int y; Point(void) { x = 0; y = 0; } Point(int x, int y) { this->x = x; this->y = y; } int weight(void) const{ return squ(this->x - C) + squ(this->y - C) + 1; } bool operator<(const Point other) const { int a = this->weight(); int b = other.weight(); if (a == b) { return tie(this->x, this->y) < tie(other.x, other.y); } return a > b; } }; //時間計測系 using chrono::duration_cast; using chrono::milliseconds; using chrono::system_clock; inline int getnow() { return duration_cast(system_clock::now().time_since_epoch()).count(); } //乱数系 mt19937 mt{ random_device{}() }; uniform_int_distribution<> rand03(0, 3); //3点から長方形の最後の1点を得る Point* getRectangle(V& point_vec) { assert(point_vec.size() == 3); set xst = {}; set yst = {}; set pst = {}; for (Point p : point_vec) { pst.insert(p); xst.insert(p.x); yst.insert(p.y); } if (xst.size() != 2 or yst.size() != 2) { return NULL; } int x, y; itrep(itr_x, xst) { itrep(itr_y, yst) { Point p = Point(*itr_x, *itr_y); if (pst.find(p) != pst.end()) { continue; } x = p.x; y = p.y; } } Point* ret = new Point(x, y); return ret; } //4点を長方形をなすように並び替えた配列を得る V getSortedRectangle(V& point_vec) { assert(point_vec.size() == 4); V ret = { point_vec[0] }; set pst = { point_vec[0] }; rep(i, 0, 3) { for (Point p : point_vec) { if (p.x != ret[i].x and p.y != ret[i].y) { continue; } if (pst.find(p) != pst.end()) { continue; } ret.push_back(p); pst.insert(p); break; } } /*if (ret.size() != 4) { cout << "point_vec" << endl; for (Point p : point_vec) { cout << p.x << ' ' << p.y << endl; } cout << "pst" << endl; itrep(itr, pst) { Point p = *itr; cout << p.x << ' ' << p.y << endl; } cout << "ret" << endl; for (Point p : ret) { cout << p.x << ' ' << p.y << endl; } assert(false); }*/ return ret; } //長方形の最後の1点を調べて追加(まっすぐ) void addStraightRectangle(int i, int j, int k) { V point_vec = { points[i],points[j],points[k] }; Point* p = getRectangle(point_vec); if (p == NULL or xbt[p->y].sum(p->x, p->x + 1) != 0) { return; } V vec = { *p,points[i],points[j],points[k] }; vec = getSortedRectangle(vec); //作る長方形が条件を満たしているかチェック vec.push_back(*p); bool flag = true; rep(l, 0, 4) { //x座標が同じ場合 if (vec[l].x == vec[l + 1].x) { int x = vec[l].x; int y1 = min(vec[l].y, vec[l + 1].y); int y2 = max(vec[l].y, vec[l + 1].y); flag &= (ybt[x].sum(y1 + 1, y2) == 0) & (lybt[x].sum(y1, y2) == 0); } //y座標が同じ場合 if (vec[l].y == vec[l + 1].y) { int y = vec[l].y; int x1 = min(vec[l].x, vec[l + 1].x); int x2 = max(vec[l].x, vec[l + 1].x); flag &= (xbt[y].sum(x1 + 1, x2) == 0) & (lxbt[y].sum(x1, x2) == 0); } if (flag == false) { break; } } if (flag == false) { return; } //長方形・点の追加 rep(l, 0, 4) { //x座標が同じ場合 if (vec[l].x == vec[l + 1].x) { int x = vec[l].x; int y1 = min(vec[l].y, vec[l + 1].y); int y2 = max(vec[l].y, vec[l + 1].y); rep(y, y1, y2) { lybt[x].add(y, 1); } } //y座標が同じ場合 if (vec[l].y == vec[l + 1].y) { int y = vec[l].y; int x1 = min(vec[l].x, vec[l + 1].x); int x2 = max(vec[l].x, vec[l + 1].x); rep(x, x1, x2) { lxbt[y].add(x, 1); } } } vec.pop_back(); ans.push_back(vec); points.push_back(*p); xbt[p->y].add(p->x, 1); ybt[p->x].add(p->y, 1); int nx = p->x + p->y, ny = p->x - p->y + (N - 1); nxbt[ny].add(nx, 1); nybt[nx].add(ny, 1); M++; } //長方形の最後の1点を調べて追加(斜め) void addDiagonalRectangle(int i, int j, int k) { Point p1 = Point(points[i].x + points[i].y, points[i].x - points[i].y + (N - 1)); Point p2 = Point(points[j].x + points[j].y, points[j].x - points[j].y + (N - 1)); Point p3 = Point(points[k].x + points[k].y, points[k].x - points[k].y + (N - 1)); V point_vec = { p1,p2,p3 }; Point* p = getRectangle(point_vec); if (p == NULL or nxbt[p->y].sum(p->x, p->x + 1) != 0) { return; } V vec = { *p,p1,p2,p3 }; vec = getSortedRectangle(vec); //作る長方形が条件を満たしているかチェック vec.push_back(*p); bool flag = true; rep(l, 0, 4) { //x座標が同じ場合 if (vec[l].x == vec[l + 1].x) { int x = vec[l].x; int y1 = min(vec[l].y, vec[l + 1].y); int y2 = max(vec[l].y, vec[l + 1].y); flag &= (nybt[x].sum(y1 + 1, y2) == 0) & (lnybt[x].sum(y1, y2) == 0); } //y座標が同じ場合 if (vec[l].y == vec[l + 1].y) { int y = vec[l].y; int x1 = min(vec[l].x, vec[l + 1].x); int x2 = max(vec[l].x, vec[l + 1].x); flag &= (nxbt[y].sum(x1 + 1, x2) == 0) & (lnxbt[y].sum(x1, x2) == 0); } if (flag == false) { break; } } if (flag == false) { return; } //長方形・点の追加 rep(l, 0, 4) { //x座標が同じ場合 if (vec[l].x == vec[l + 1].x) { int x = vec[l].x; int y1 = min(vec[l].y, vec[l + 1].y); int y2 = max(vec[l].y, vec[l + 1].y); rep(y, y1, y2) { lnybt[x].add(y, 1); } } //y座標が同じ場合 if (vec[l].y == vec[l + 1].y) { int y = vec[l].y; int x1 = min(vec[l].x, vec[l + 1].x); int x2 = max(vec[l].x, vec[l + 1].x); rep(x, x1, x2) { lnxbt[y].add(x, 1); } } } vec.pop_back(); nxbt[p->y].add(p->x, 1); nybt[p->x].add(p->y, 1); //まっすぐの点に直す V nvec = {}; for (Point np : vec) { int x = (np.x + np.y - (N - 1)) >> 1; int y = (np.x - np.y + (N - 1)) >> 1; nvec.push_back({ x,y }); } ans.push_back(nvec); int x = (p->x + p->y - (N - 1)) >> 1; int y = (p->x - p->y + (N - 1)) >> 1; points.push_back({ x,y }); xbt[y].add(x, 1); ybt[x].add(y, 1); M++; } //メイン int main(void) { //時間計測開始 int time_limit = getnow() + TIME; //入力 cin >> N >> M; C = (N - 1) >> 1; points = V(M); xbt = V >(N, BIT(N)); ybt = V >(N, BIT(N)); nxbt = V >(N << 1, BIT(N << 1)); nybt = V >(N << 1, BIT(N << 1)); rep(i, 0, M) { int x, y; cin >> x >> y; xbt[y].add(x, 1); ybt[x].add(y, 1); points[i] = Point(x, y); int nx = x + y, ny = x - y + (N - 1); nxbt[ny].add(nx, 1); nybt[nx].add(ny, 1); } //sort(all(points)); //既に追加した線分の集合 lxbt = V >(N, BIT(N - 1)); lybt = V >(N, BIT(N - 1)); lnxbt = V >(N << 1, BIT((N << 1) - 1)); lnybt = V >(N << 1, BIT((N << 1) - 1)); //時間の許す限り探索 while (true) { //3点の組み合わせを調べる rep(i, 0, M - 2) { rep(j, i + 1, M - 1) { rep(k, j + 1, M) { //時間の許す限り探索 if (getnow() >= time_limit) { goto OUTPUT; } //長方形の最後の1点を調べて追加(まっすぐ) addStraightRectangle(i, j, k); //長方形の最後の1点を調べて追加(斜め) addDiagonalRectangle(i, j, k); } } } } OUTPUT: //答えを出力 cout << ans.size() << endl; for (V vec : ans) { cout << vec[0].x << ' ' << vec[0].y; rep(i, 1, 4) { cout << ' ' << vec[i].x << ' ' << vec[i].y; } cout << '\n'; } return 0; }