/** * date : 2021-12-06 15:02:34 */ #define NDEBUG using namespace std; // intrinstic #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // utility namespace Nyaan { using ll = long long; using i64 = long long; using u64 = unsigned long long; using i128 = __int128_t; using u128 = __uint128_t; template using V = vector; template using VV = vector>; using vi = vector; using vl = vector; using vd = V; using vs = V; using vvi = vector>; using vvl = vector>; template struct P : pair { template P(Args... args) : pair(args...) {} using pair::first; using pair::second; T &x() { return first; } const T &x() const { return first; } U &y() { return second; } const U &y() const { return second; } P &operator+=(const P &r) { first += r.first; second += r.second; return *this; } P &operator-=(const P &r) { first -= r.first; second -= r.second; return *this; } P &operator*=(const P &r) { first *= r.first; second *= r.second; return *this; } P operator+(const P &r) const { return P(*this) += r; } P operator-(const P &r) const { return P(*this) -= r; } P operator*(const P &r) const { return P(*this) *= r; } P operator*(int r) const { return {first * r, second * r}; } P operator-() const { return P{-first, -second}; } }; using pl = P; using pi = P; using vp = V; constexpr int inf = 1001001001; constexpr long long infLL = 4004004004004004004LL; template int sz(const T &t) { return t.size(); } template inline bool amin(T &x, U y) { return (y < x) ? (x = y, true) : false; } template inline bool amax(T &x, U y) { return (x < y) ? (x = y, true) : false; } template inline T Max(const vector &v) { return *max_element(begin(v), end(v)); } template inline T Min(const vector &v) { return *min_element(begin(v), end(v)); } template inline long long Sum(const vector &v) { return accumulate(begin(v), end(v), 0LL); } template int lb(const vector &v, const T &a) { return lower_bound(begin(v), end(v), a) - begin(v); } template int ub(const vector &v, const T &a) { return upper_bound(begin(v), end(v), a) - begin(v); } constexpr long long TEN(int n) { long long ret = 1, x = 10; for (; n; x *= x, n >>= 1) ret *= (n & 1 ? x : 1); return ret; } template pair mkp(const T &t, const U &u) { return make_pair(t, u); } template vector mkrui(const vector &v, bool rev = false) { vector ret(v.size() + 1); if (rev) { for (int i = int(v.size()) - 1; i >= 0; i--) ret[i] = v[i] + ret[i + 1]; } else { for (int i = 0; i < int(v.size()); i++) ret[i + 1] = ret[i] + v[i]; } return ret; }; template vector mkuni(const vector &v) { vector ret(v); sort(ret.begin(), ret.end()); ret.erase(unique(ret.begin(), ret.end()), ret.end()); return ret; } template vector mkord(int N, F f) { vector ord(N); iota(begin(ord), end(ord), 0); sort(begin(ord), end(ord), f); return ord; } template vector mkinv(vector &v) { int max_val = *max_element(begin(v), end(v)); vector inv(max_val + 1, -1); for (int i = 0; i < (int)v.size(); i++) inv[v[i]] = i; return inv; } } // namespace Nyaan // bit operation namespace Nyaan { __attribute__((target("popcnt"))) inline int popcnt(const u64 &a) { return _mm_popcnt_u64(a); } inline int lsb(const u64 &a) { return a ? __builtin_ctzll(a) : 64; } inline int ctz(const u64 &a) { return a ? __builtin_ctzll(a) : 64; } inline int msb(const u64 &a) { return a ? 63 - __builtin_clzll(a) : -1; } template inline int gbit(const T &a, int i) { return (a >> i) & 1; } template inline void sbit(T &a, int i, bool b) { if (gbit(a, i) != b) a ^= T(1) << i; } constexpr long long PW(int n) { return 1LL << n; } constexpr long long MSK(int n) { return (1LL << n) - 1; } } // namespace Nyaan // inout namespace Nyaan { template ostream &operator<<(ostream &os, const pair &p) { os << p.first << " " << p.second; return os; } template istream &operator>>(istream &is, pair &p) { is >> p.first >> p.second; return is; } template ostream &operator<<(ostream &os, const vector &v) { int s = (int)v.size(); for (int i = 0; i < s; i++) os << (i ? " " : "") << v[i]; return os; } template istream &operator>>(istream &is, vector &v) { for (auto &x : v) is >> x; return is; } void in() {} template void in(T &t, U &... u) { cin >> t; in(u...); } void out() { cout << "\n"; } template void out(const T &t, const U &... u) { cout << t; if (sizeof...(u)) cout << sep; out(u...); } void outr() {} template void outr(const T &t, const U &... u) { cout << t; outr(u...); } struct IoSetupNya { IoSetupNya() { cin.tie(nullptr); ios::sync_with_stdio(false); cout << fixed << setprecision(15); cerr << fixed << setprecision(7); } } iosetupnya; } // namespace Nyaan // debug namespace DebugImpl { template struct is_specialize : false_type {}; template struct is_specialize< U, typename conditional::type> : true_type {}; template struct is_specialize< U, typename conditional::type> : true_type {}; template struct is_specialize::value, void>> : true_type { }; void dump(const char& t) { cerr << t; } void dump(const string& t) { cerr << t; } void dump(const bool& t) { cerr << (t ? "true" : "false"); } template ::value, nullptr_t> = nullptr> void dump(const U& t) { cerr << t; } template void dump(const T& t, enable_if_t::value>* = nullptr) { string res; if (t == Nyaan::inf) res = "inf"; if constexpr (is_signed::value) { if (t == -Nyaan::inf) res = "-inf"; } if constexpr (sizeof(T) == 8) { if (t == Nyaan::infLL) res = "inf"; if constexpr (is_signed::value) { if (t == -Nyaan::infLL) res = "-inf"; } } if (res.empty()) res = to_string(t); cerr << res; } template void dump(const pair&); template void dump(const pair&); template void dump(const T& t, enable_if_t::value>* = nullptr) { cerr << "[ "; for (auto it = t.begin(); it != t.end();) { dump(*it); cerr << (++it == t.end() ? "" : ", "); } cerr << " ]"; } template void dump(const pair& t) { cerr << "( "; dump(t.first); cerr << ", "; dump(t.second); cerr << " )"; } template void dump(const pair& t) { cerr << "[ "; for (int i = 0; i < t.second; i++) { dump(t.first[i]); cerr << (i == t.second - 1 ? "" : ", "); } cerr << " ]"; } void trace() { cerr << endl; } template void trace(Head&& head, Tail&&... tail) { cerr << " "; dump(head); if (sizeof...(tail) != 0) cerr << ","; trace(forward(tail)...); } } // namespace DebugImpl #ifdef NyaanDebug #define trc(...) \ do { \ cerr << "## " << #__VA_ARGS__ << " = "; \ DebugImpl::trace(__VA_ARGS__); \ } while (0) #else #define trc(...) (void(0)) #endif // macro #define each(x, v) for (auto&& x : v) #define each2(x, y, v) for (auto&& [x, y] : v) #define all(v) (v).begin(), (v).end() #define rep(i, N) for (long long i = 0; i < (long long)(N); i++) #define repr(i, N) for (long long i = (long long)(N)-1; i >= 0; i--) #define rep1(i, N) for (long long i = 1; i <= (long long)(N); i++) #define repr1(i, N) for (long long i = (N); (long long)(i) > 0; i--) #define reg(i, a, b) for (long long i = (a); i < (b); i++) #define regr(i, a, b) for (long long i = (b)-1; i >= (a); i--) #define fi first #define se second #define ini(...) \ int __VA_ARGS__; \ in(__VA_ARGS__) #define inl(...) \ long long __VA_ARGS__; \ in(__VA_ARGS__) #define ins(...) \ string __VA_ARGS__; \ in(__VA_ARGS__) #define in2(s, t) \ for (int i = 0; i < (int)s.size(); i++) { \ in(s[i], t[i]); \ } #define in3(s, t, u) \ for (int i = 0; i < (int)s.size(); i++) { \ in(s[i], t[i], u[i]); \ } #define in4(s, t, u, v) \ for (int i = 0; i < (int)s.size(); i++) { \ in(s[i], t[i], u[i], v[i]); \ } #define die(...) \ do { \ Nyaan::out(__VA_ARGS__); \ return; \ } while (0) namespace Nyaan { void solve(); } int main() { Nyaan::solve(); } // struct UnionFind { vector data; UnionFind(int N) : data(N, -1) {} int find(int k) { return data[k] < 0 ? k : data[k] = find(data[k]); } int unite(int x, int y) { if ((x = find(x)) == (y = find(y))) return false; if (data[x] > data[y]) swap(x, y); data[x] += data[y]; data[y] = x; return true; } // f ... merge function template int unite(int x, int y,const F &f) { if ((x = find(x)) == (y = find(y))) return false; if (data[x] > data[y]) swap(x, y); data[x] += data[y]; data[y] = x; f(x, y); return true; } int size(int k) { return -data[find(k)]; } int same(int x, int y) { return find(x) == find(y); } }; /** * @brief Union Find(Disjoint Set Union) * @docs docs/data-structure/union-find.md */ template struct edge { int src, to; T cost; edge(int _to, T _cost) : src(-1), to(_to), cost(_cost) {} edge(int _src, int _to, T _cost) : src(_src), to(_to), cost(_cost) {} edge &operator=(const int &x) { to = x; return *this; } operator int() const { return to; } }; template using Edges = vector>; template using WeightedGraph = vector>; using UnweightedGraph = vector>; // Input of (Unweighted) Graph UnweightedGraph graph(int N, int M = -1, bool is_directed = false, bool is_1origin = true) { UnweightedGraph g(N); if (M == -1) M = N - 1; for (int _ = 0; _ < M; _++) { int x, y; cin >> x >> y; if (is_1origin) x--, y--; g[x].push_back(y); if (!is_directed) g[y].push_back(x); } return g; } // Input of Weighted Graph template WeightedGraph wgraph(int N, int M = -1, bool is_directed = false, bool is_1origin = true) { WeightedGraph g(N); if (M == -1) M = N - 1; for (int _ = 0; _ < M; _++) { int x, y; cin >> x >> y; T c; cin >> c; if (is_1origin) x--, y--; g[x].emplace_back(x, y, c); if (!is_directed) g[y].emplace_back(y, x, c); } return g; } // Input of Edges template Edges esgraph(int N, int M, int is_weighted = true, bool is_1origin = true) { Edges es; for (int _ = 0; _ < M; _++) { int x, y; cin >> x >> y; T c; if (is_weighted) cin >> c; else c = 1; if (is_1origin) x--, y--; es.emplace_back(x, y, c); } return es; } // Input of Adjacency Matrix template vector> adjgraph(int N, int M, T INF, int is_weighted = true, bool is_directed = false, bool is_1origin = true) { vector> d(N, vector(N, INF)); for (int _ = 0; _ < M; _++) { int x, y; cin >> x >> y; T c; if (is_weighted) cin >> c; else c = 1; if (is_1origin) x--, y--; d[x][y] = c; if (!is_directed) d[y][x] = c; } return d; } using namespace Nyaan; // https://github.com/not522/CompetitiveProgramming/blob/master/include/math/sat.hpp class SatSolver { private: vector>> cl; map, vector> w; vector reason, level, que, activity; int n, qi; void enqueue(int v, bool a, int r = -1) { assigns[v] = a; reason[v] = r; level[v] = (que.empty() ? 1 : level[que.back()]); que.emplace_back(v); } void add(const vector> &clause) { for (auto l : clause) { w[l].emplace_back(cl.size()); } cl.emplace_back(clause); } void analyze(int confl) { int i = que.size(), lv = 1; unordered_set used; vector> learnt; for (int cnt = 0; cnt || used.empty(); --cnt) { for (auto q : cl[confl]) { if (!used.emplace(q.first).second) { continue; } activity[q.first] += 1e5; if (level[q.first] == level[que.back()]) { ++cnt; } else { learnt.emplace_back(q); lv = max(lv, level[q.first]); } } while (!used.count(que[--i])) { ; } confl = reason[que[i]]; } learnt.emplace_back(que[i], !assigns[que[i]]); for (; !que.empty() && level[que.back()] > lv; que.pop_back()) { level[que.back()] = 0; } qi = que.size(); enqueue(learnt.back().first, learnt.back().second, cl.size()); add(learnt); } int propagate() { for (; qi < int(que.size()); ++qi) { for (int cr : w[make_pair(que[qi], !assigns[que[qi]])]) { int cnt = 0; for (auto &lit : cl[cr]) { if (level[lit.first] == 0) { activity[lit.first] += 1e3; swap(lit, cl[cr][0]); ++cnt; } else if (assigns[lit.first] == lit.second) { swap(lit, cl[cr][0]); cnt = -1; break; } } if (cnt == 0) { return cr; } if (cnt == 1) { enqueue(cl[cr][0].first, cl[cr][0].second, cr); } } } return -1; } public: vector assigns; SatSolver(int _n, const vector>> &clauses) : reason(_n), level(_n), activity(_n), n(_n), qi(0), assigns(_n) { for (const auto &clause : clauses) { add(clause); } } bool solve() { while (true) { int confl = propagate(); if (confl != -1) { if (level[que.back()] == 1u) { return false; } for (auto &a : activity) { a /= 1.05; } analyze(confl); } else { int k = -1; for (int i = 0; i < n; ++i) { if (level[i] == 0 && (k == -1 || activity[k] < activity[i])) { k = i; } } if (k == -1) { return true; } enqueue(k, assigns[k]); ++level[k]; } } } }; struct Plane { vector nodes; int index; static int all_index; Plane() : index(-1) {} bool in(int u) const { return find(all(nodes), u) != end(nodes); } bool in(int u, int v) const { return in(u) and in(v); } bool edge_in(int u, int v) const { if (u > v) swap(u, v); auto itu = find(all(nodes), u); auto itv = find(all(nodes), v); if (itu == end(nodes) or itv == end(nodes)) return false; if (itu + 1 == itv) return true; if (itv + 1 == end(nodes) and itu == begin(nodes)) return true; return false; } // 破壊的 Plane split(int u, int v) { assert(in(u, v) and !edge_in(u, v)); Plane chd; chd.set_index(); if (u > v) swap(u, v); auto itl = find(all(nodes), u); auto itr = find(all(nodes), v); chd.nodes = {itl, itr + 1}; nodes.erase(itl + 1, itr); return chd; } void set_index() { index = all_index++; } friend ostream &operator<<(ostream &os, const Plane &p) { os << endl; os << "index : " << p.index << endl; os << "nodes : " << p.nodes << endl; os << endl; return os; } }; int Plane::all_index = 0; void Nyaan::solve() { inl(N, M); auto g = graph(N, M); // 外周 Plane soto; soto.set_index(); Plane naka; naka.set_index(); { vi init(N); iota(all(init), 0); soto.nodes = naka.nodes = init; } vector ps; ps.push_back(naka); rep(i, N) each(j, g[i]) { if (i > j) continue; each(p, ps) { if (p.in(i, j)) { auto ch = p.split(i, j); ps.push_back(ch); break; } } } vp pg_es; auto es_push = [&](int i, int j) { if (i > j) swap(i, j); pg_es.emplace_back(i, j); }; rep(i, N) { int j = (i + 1) % N; each(p, ps) { if (p.edge_in(i, j)) { es_push(0, p.index); break; } } } rep(i, N) each(j, g[i]) { int p1 = -1, p2 = -1; rep(k, sz(ps)) { if (ps[k].edge_in(i, j)) { p1 = k; break; } } reg(k, p1 + 1, sz(ps)) { if (ps[k].edge_in(i, j)) { p2 = k; break; } } if (p1 != -1 and p2 != -1) es_push(ps[p1].index, ps[p2].index); } pg_es = mkuni(pg_es); ps.insert(begin(ps), soto); trc(pg_es); // SAT を書けばよい // SatSolver sat((N + M) * 4, {}); M = sz(ps); using Clause = vector>; V clauses; rep(i, N + M) { Clause c; rep(j, 4) c.emplace_back(i * 4 + j, true); clauses.push_back(c); /* rep(j, 4) rep(k, j) { Clause c2; c2.emplace_back(i * 4 + j, false); c2.emplace_back(i * 4 + k, false); clauses.push_back(c2); } */ } // a と b は異なる色 auto add = [&](int a, int b) { trc(a, b); rep(j, 4) { // a が j 色 -> b は not j Clause c; c.emplace_back(a * 4 + j, false); c.emplace_back(b * 4 + j, false); clauses.push_back(c); } }; // 頂点同士 rep(i, N) { each(j, g[i]) add(i, j); add(i, (i + 1) % N); } // 面同士 each2(i, j, pg_es) add(i + N, j + N); // 面と頂点 each(p, ps) { int i = p.index + N; each(j, p.nodes) add(i, j); } SatSolver sat((N + M) * 4, clauses); auto sol = sat.solve(); trc(sol); out(sol ? 4 : 5); /* // すべて 偶数か? bool all_even = true; each(p, ps) { if (sz(p.nodes) % 2 != 0) all_even = false; } if (all_even) { // 二部グラフか? int NN = sz(ps); UnionFind uf(2 * NN); each2(u, v, pg_es) { uf.unite(u, v + NN); uf.unite(v, u + NN); } bool bip = true; rep(i, NN) { if (uf.same(i, i + NN)) bip = false; } die(bip ? 4 : 5); } // 奇数同士が面で接しているか? // 接してたらダメ(無証明) bool sparse = 1; each2(u, v, pg_es) { if (sz(ps[u].nodes) % 2 == 0) continue; if (sz(ps[v].nodes) % 2 == 0) continue; sparse = 0; } if (!sparse) die(5); // わかっていること // 周上に奇数頂点ある平面 -> (奇数) のように表す // - 答えは 4 か 5 (無証明) // - (奇数) はすべて辺で接していない // (奇数) を全て同色で塗れれば 4 が達成できる // -> 頂点・(偶数) を三色で塗り分けられるか? // (奇数) 同士は隣り合っていない // -> すべての頂点は(偶数)に属している */ }