#line 1 "playspace/main.cpp" #include #line 6 "library/gandalfr/graph/graph.hpp" #line 3 "library/gandalfr/data_structure/union_find.hpp" #line 6 "library/gandalfr/data_structure/union_find.hpp" class union_find { private: int N; mutable std::vector par; std::vector nxt; int group_num; // 集合の数 public: union_find() : N(0), group_num(0) {} union_find(int n) : N(n), par(n, -1), nxt(n), group_num(n) { std::iota(nxt.begin(), nxt.end(), 0); } /** * @brief 頂点を n 個に増やす * @attention 小さくはできない */ void expand(int n) { if (n <= N) return; par.resize(n, -1); nxt.resize(n); for (int i = N; i < n; ++i) nxt[i] = i; group_num += n - N; N = n; } int leader(int x) const { return (par[x] < 0 ? x : par[x] = leader(par[x])); } bool same(int x, int y) const { return leader(x) == leader(y); } bool merge(int x, int y) { if ((x = leader(x)) == (y = leader(y))) return false; if (-par[x] > -par[y]) std::swap(x, y); par[x] += par[y]; par[y] = x; std::swap(nxt[x], nxt[y]); group_num--; return true; } /** * @brief x の属するグループのサイズを返す */ int size(int x) const { return -par[leader(x)]; } /** * @brief すべてのノードの数 */ int size() const { return N; } std::vector group_containing_node(int x) const { std::vector ret{x}; for (int cu = nxt[x]; cu != ret[0]; cu = nxt[cu]) ret.push_back(cu); return ret; } int count_groups() const { return group_num; } std::vector> all_groups() const { std::vector> result; result.reserve(group_num); std::vector used(N, false); for (int i = 0; i < N; ++i) { if (!used[i]) { result.emplace_back(group_containing_node(i)); for (int x : result.back()) { used[x] = true; } } } return result; } }; #line 3 "library/gandalfr/math/matrix.hpp" #line 8 "library/gandalfr/math/matrix.hpp" template class matrix { private: int H, W; std::valarray> table; enum rowtrans_operation_name { SCALE, SWAP, ADD }; struct rowtrans_operation { int op, tar, res; T scl; }; using operations_history = std::vector; public: matrix() = default; matrix(int _H, int _W, T val = 0) : H(_H), W(_W), table(std::valarray(val, _W), _H) {} matrix(const std::vector> &vv) : H(vv.size()), W(vv[0].size()), table(std::valarray(W), H) { for (int i = 0; i < H; i++) for (int j = 0; j < W; j++) table[i][j] = vv[i][j]; } matrix(const std::valarray> &vv) : H(vv.size()), W(vv[0].size()), table(vv) {} /** * @brief 行列をリサイズする。 * @param val 拡張部分の値 */ void resize(int _H, int _W, T val = 0) { H = _H, W = _W; table.resize(_H, std::valarray(val, _H)); } int size_H() const { return H; } int size_W() const { return W; } void transpose() { matrix ret(W, H); for (int i = 0; i < H; i++) for (int j = 0; j < W; j++) ret.table[j][i] = table[i][j]; *this = ret; } /** * @brief 第 i 行に対して行単位で代入を行う * @example A.row_assign(3, {1,2,3}); */ void row_assign(int i, const std::valarray &row) { assert(0 <= i && i < H); assert(W == (int)row.size()); table[i] = row; } /** * @brief 第 i 行, 第 j 行を入れ替える */ void row_swap(int i, int j) { assert(0 <= i && i < H); assert(0 <= j && j < H); table[i].swap(table[j]); } /** * @attention O(n^3) * @attention 整数型では正しく計算できない。double や fraction を使うこと。 * @attention 枢軸選びをしていないので double では誤差が出るかも。 */ operations_history sweep_method() { operations_history hist; for (int h = 0, w = 0; h < H && w < W; w++) { if (table[h][w] == 0) { for (int piv = h + 1; piv < H; piv++) { if (table[piv][w] != 0) { hist.push_back({SWAP, h, piv, 0}); row_swap(h, piv); break; } } if (table[h][w] == 0) { continue; } } T inv = 1 / table[h][w]; hist.push_back({SCALE, -1, w, inv}); table[h] *= inv; for (int j = h + 1; j < H; j++) { hist.push_back({ADD, h, j, -table[j][w]}); table[j] -= table[h] * table[j][w]; } h++; } return hist; } int rank() const { auto U(*this); U.sweep_method(); int r = 0; for (int i = 0; i < H; ++i) { for (int j = i; j < W; ++j) { if (U.table[i][j] != 0) { r++; break; } } } return r; } T determinant() const { assert(H == W); matrix U(*this); T det = 1; auto hist = U.sweep_method(); if (U.table[H - 1][H - 1] == 0) return 0; for (auto &[op, tar, res, scl] : hist) { switch (op) { case SCALE: det /= scl; break; case SWAP: det *= -1; break; } } return det; } std::vector solve_system_of_equations(const std::vector &y) { assert(H == W); std::vector x(y); matrix U(*this); auto hist = U.sweep_method(); if (U.table[H - 1][H - 1] == 0) return {}; for (auto &[op, tar, res, scl] : hist) { switch (op) { case SCALE: x[res] *= scl; break; case SWAP: std::swap(x[tar], x[res]); break; case ADD: x[res] += x[tar] * scl; break; } } for (int i = H - 1; i >= 0; --i) { for (int j = 0; j < i; ++j) { x[j] -= U.table[j][i] * x[i]; } } return x; } matrix inverse() const { assert(H == W); matrix INV(matrix::E(H)), U(*this); auto hist = U.sweep_method(); if (U.table[H - 1][H - 1] == 0) return matrix(0, 0); for (auto &[op, tar, res, scl] : hist) { switch (op) { case SCALE: INV.table[res] *= scl; break; case SWAP: std::swap(INV.table[tar], INV.table[res]); break; case ADD: INV.table[res] += INV.table[tar] * scl; break; } } for (int i = H - 1; i >= 0; --i) { for (int j = 0; j < i; ++j) { INV.table[j] -= INV.table[i] * U.table[j][i]; } } return INV; } void print() const { for (int i = 0; i < H; i++) { for (int j = 0; j < W; j++) { std::cout << table[i][j] << (j == W - 1 ? "" : " "); } std::cout << std::endl; } } matrix &operator+=(const matrix &a) { this->table += a.table; return *this; } matrix &operator-=(const matrix &a) { this->table -= a.table; return *this; } matrix &operator*=(const T &a) { this->table *= a; return *this; } matrix &operator*=(const matrix &a) { assert(W == a.H); matrix a_t(a), ret(H, a.W); a_t.transpose(); for (int i = 0; i < H; i++) { for (int j = 0; j < a_t.H; j++) { ret.table[i][j] = (table[i] * a_t.table[j]).sum(); } } return *this = ret; } matrix &operator/=(const T &a) { this->table /= a; return *this; } /** * @brief 行列の冪乗。 * @param n 指数 * @attention n が 0 なら単位行列。 * @attention 演算子の優先度に注意。 */ matrix operator^=(long long n) { assert(H == W); if (n == 0) return *this = E(H); n--; matrix x(*this); while (n) { if (n & 1) *this *= x; x *= x; n >>= 1; } return *this; } matrix operator+() const { return *this; } matrix operator-() const { return matrix(*this) *= -1; } matrix operator+(const matrix &a) const { return matrix(*this) += a; } matrix operator-(const matrix &a) const { return matrix(*this) -= a; } matrix operator*(const T &a) { return matrix(*this) *= a; } matrix operator*(const matrix &a) const { return matrix(*this) *= a; } matrix operator/(const T &a) const { return matrix(*this) /= a; } matrix operator^(long long n) const { return matrix(*this) ^= n; } friend std::istream &operator>>(std::istream &is, matrix &mt) { for (auto &arr : mt.table) for (auto &x : arr) is >> x; return is; } const T &operator()(int h, int w) const { assert(0 <= h && h < H && 0 <= w && w <= W); return table[h][w]; } T &operator()(int h, int w) { assert(0 <= h && h < H && 0 <= w && w <= W); return table[h][w]; } template bool operator==(const matrix &other) { if (size_H() != other.size_H() || size_W() != other.size_W()) return false; for (int h = 0; h < H; ++h) { for (int w = 0; w < W; ++w) { if (table[h][w] != other.table[h][w]) return false; } } return true; } template bool operator!=(const matrix &other) { return !operator==(other); } /** * @brief サイズ n の単位行列。 */ static matrix E(int N) { matrix ret(N, N); for (int i = 0; i < N; i++) ret.table[i][i] = 1; return ret; } }; #line 7 "library/gandalfr/graph/base_graph.hpp" namespace internal { template struct _base_edge { int v[2]; Weight cost; int id; _base_edge() {} _base_edge(int from, int to, Weight cost, int id) : v{from, to}, cost(cost), id(id) {} // x から見た反対側の端点を返す int opp(int x) { if (x == v[0]) { return v[1]; } else if (x == v[1]) { return v[0]; } else { std::abort(); } } friend std::ostream &operator<<(std::ostream &os, const _base_edge &e) { e.print(os); return os; } protected: virtual void print(std::ostream &os) const = 0; }; } // namespace internal template struct edge : public internal::_base_edge { using internal::_base_edge::_base_edge; edge reverse() const { return {this->v[1], this->v[0], this->cost, this->id}; } protected: void print(std::ostream &os) const override { os << this->v[0] << " " << this->v[1] << " " << this->cost; } }; template <> struct edge : public internal::_base_edge { static inline const int cost = 1; using internal::_base_edge::_base_edge; edge(int _from, int _to, int _id) : _base_edge(_from, _to, 1, _id) {} edge reverse() const { return {v[1], v[0], 1, id}; } protected: void print(std::ostream &os) const override { os << this->v[0] << " " << this->v[1]; } }; template struct flow_edge : public internal::_base_edge { private: Flow res, cap; using internal::_base_edge::cost; public: flow_edge() {} flow_edge(int from, int to, Flow res, Flow cap, int id) : internal::_base_edge(from, to, 1, id), res(res), cap(cap) {} flow_edge(int from, int to, Flow res, Flow cap, Cost cost, int id) : internal::_base_edge(from, to, cost, id), res(res), cap(cap) { } // x から見たコスト Cost get_cost(int x) { if (x == this->v[0]) { return this->cost; } else if (x == this->v[1]) { return -this->cost; } else { std::abort(); } } flow_edge reverse() const { return {this->v[1], this->v[0], cap - res, cap, this->cost, this->id}; } // x から見た残余 Flow residual(int x) const { if (x == this->v[0]) { return res; } else if (x == this->v[1]) { return cap - res; } else { std::abort(); } } // x から見て残余がゼロか? Flow is_full(int x) const { if (x == this->v[0]) { return res == 0; } else if (x == this->v[1]) { return cap - res == 0; } else { std::abort(); } } // x から流量を d だけ追加 void add_flow(int x, Flow d) { if (x == this->v[0]) { res -= d; } else if (x == this->v[1]) { res += d; } else { std::abort(); } } protected: void print(std::ostream &os) const override { os << this->v[0] << " " << this->v[1] << " " << cap - res << "/" << cap; } }; namespace internal { template class _base_graph { protected: int N; std::vector> G; std::vector> E; public: _base_graph(){}; _base_graph(int n) : N(n), G(n){}; _base_graph(int n, int m) : N(n), G(n) { E.reserve(m); }; /** * @return ノードの数 */ int count_nodes() const { return N; } /** * @return 辺の数 */ int count_edges() const { return E.size(); } /** * @param n ノード番号 * @return ノード n からの隣接頂点のリストの const 参照 */ const std::vector &operator[](int n) const { return G[n]; } /** * @return グラフ全体の辺のポインタのリストの const 参照 */ const std::vector> &edges() const { return E; } void print() const { std::cout << this->N << " " << this->E.size() << std::endl; for (auto &e : this->E) std::cout << *e << std::endl; } }; } // namespace internal #line 10 "library/gandalfr/graph/graph.hpp" /** * @brief グラフを管理するクラス。 * @tparam Weight int なら重みなし、そうでないなら重みつきグラフ * @tparam is_directed 有向グラフかとうか */ template class graph : public internal::_base_graph> { private: union_find uf; mutable std::vector visited; // dfs / bfs のための領域 Weight W = 0; bool forest_flag = true; static inline const Weight WEIGHT_MAX = std::numeric_limits::max(); void reset_visited_flag(int node) const { for (int x : uf.group_containing_node(node)) visited[x] = false; } void reset_visited_flag() const { visited.assign(this->N, false); } public: graph() {} graph(int n) : internal::_base_graph>(n), uf(n), visited(n) {} graph(int n, int m) : internal::_base_graph>(n, m), uf(n), visited(n) {} graph(const graph &other) : graph(other.N) { for (auto &e : other.E) { add_edge(*e); } } /** * @brief ノードの数をn個まで増やす * @param n サイズ * @attention 今のノード数より小さい数を渡したとき、変化なし */ void expand(int n) { if (n <= this->N) return; this->N = n; this->G.resize(n); visited.resize(n); uf.expand(n); } /** * @return 木か */ bool is_tree() const { return forest_flag && uf.count_groups() == 1; } /** * @return 森か */ bool is_forest() const { return forest_flag; } /** * @return グラフの重み */ Weight weight() const { return W; } /** * @param e 辺 * @attention 渡した辺の id は保持される */ void add_edge(const edge &e) { forest_flag &= uf.merge(e.v[0], e.v[1]); this->E.emplace_back(std::make_unique>(e)); this->G[e.v[0]].push_back(this->E.back().get()); if (!is_directed && e.v[0] != e.v[1]) this->G[e.v[1]].push_back(this->E.back().get()); W += e.cost; } /** * @attention 辺の id は、(現在の辺の本数)番目 が振られる * @attention WEIGHT が int だとエラー */ void add_edge(int from, int to, Weight cost) { static_assert(!std::is_same::value); add_edge({from, to, cost, (int)this->E.size()}); } /** * @attention 辺の id は、(現在の辺の本数)番目 が振られる * @attention WEIGHT が int 以外だとエラー */ void add_edge(int from, int to) { static_assert(std::is_same::value); add_edge({from, to, (int)this->E.size()}); } /** * @param x ノード番号 * @param y ノード番号 * @return x, y が連結かどうか */ bool are_connected(int x, int y) const { return uf.same(x, y); } /** * @return 弱連結成分の数 */ int count_connected_components() const { return uf.count_groups(); } /** * @return 弱連結成分のリストのリスト */ std::vector> weakly_connected_components() const { return uf.all_groups(); } /** * @brief ノード x が含まれている弱連結成分のリストを返す */ std::vector component_containing_node(int x) { return uf.group_containing_node(x); } /** * @brief グラフを連結なグラフに分けてリストにして返す * @example auto[Gs, gr, nd] = G.decompose(); * @returns * 1.グラフのリスト * 2.各ノードがグラフのリストの何番目に属するか * 3.各ノードがグラフのどのノードになっているか */ std::tuple, std::vector, std::vector> decompose() const { std::vector Gs(uf.count_groups()); std::vector> groups(uf.all_groups()); std::vector group_id(this->N), node_id(this->N); for (int i = 0; i < (int)groups.size(); i++) { Gs[i].expand(groups[i].size()); for (int j = 0; j < (int)groups[i].size(); j++) { group_id[groups[i][j]] = i; node_id[groups[i][j]] = j; } } for (auto &e : this->E) { int id = group_id[e->v[0]]; e->v[0] = node_id[e->v[0]]; e->v[1] = node_id[e->v[1]]; Gs[id].add_edge(e); } return std::make_tuple(std::move(Gs), std::move(group_id), std::move(node_id)); } /** * @brief グラフを隣接行列に変換 * @param invalid 辺のないときの値 * @attention 自己ループが含まれていない限り、対角成分は 0 * @attention 多重辺を持たないと仮定 */ matrix to_adjajency(Weight invalid = 0) const { matrix ret(this->N, this->N, invalid); for (int i = 0; i < this->N; i++) ret(i, i) = 0; for (auto &e : this->E) { ret(e->v[0], e->v[1]) = e->cost; if constexpr (!is_directed) { ret(e->v[1], e->v[0]) = e->cost; } } return ret; } private: using PAIR = std::pair; using Dijkstra_queue = std::priority_queue, std::greater>; void run_bfs(std::vector &dist, std::queue &q) const { while (!q.empty()) { int cu = q.front(); q.pop(); for (auto &e : this->G[cu]) { int to = e->opp(cu); if (dist[to] != WEIGHT_MAX) continue; dist[to] = dist[cu] + 1; q.push(to); } } } void run_Dijkstra(std::vector &dist, Dijkstra_queue &q) const { while (!q.empty()) { Weight cur_dist = q.top().first; int cu = q.top().second; q.pop(); if (visited[cu]) continue; visited[cu] = true; for (auto &e : this->G[cu]) { int to = e->opp(cu); Weight alt = cur_dist + e->cost; if (dist[to] <= alt) continue; dist[to] = alt; q.push({alt, to}); } } } public: /** * @brief 最短距離を計算する * @param start_node 始点 * @param invalid 到達不能な頂点に格納される値 * @return 各ノードまでの最短距離のリスト */ std::vector distances(int start_node, Weight invalid) const { std::vector dist(this->N, WEIGHT_MAX); dist[start_node] = 0; if constexpr (std::is_same::value) { // BFS algorithm std::queue q; q.push(start_node); run_bfs(dist, q); } else { // Dijkstra's algorithm Dijkstra_queue q; q.push({0, start_node}); reset_visited_flag(start_node); run_Dijkstra(dist, q); } for (auto &x : dist) if (x == WEIGHT_MAX) x = invalid; return dist; } matrix distances_from_all_nodes(Weight invalid = -1) { auto mt(to_adjajency(WEIGHT_MAX)); for (int k = 0; k < this->N; k++) // 経由する頂点 for (int i = 0; i < this->N; i++) // 始点 for (int j = 0; j < this->N; j++) // 終点 if (mt(i, k) != WEIGHT_MAX && mt(k, j) != WEIGHT_MAX) mt(i, j) = std::min(mt(i, j), mt(i, k) + mt(k, j)); for (int i = 0; i < this->N; ++i) for (int j = 0; j < this->N; ++j) if (mt(i, j) == WEIGHT_MAX) mt(i, j) = invalid; return mt; } /** * @brief 復元付き最短経路 * @attention 到達可能でないとき、空の配列で返る */ std::vector> shortest_path(int start_node, int end_node) { if (start_node == end_node) return {}; auto dist = distances(start_node, WEIGHT_MAX); if (dist[end_node] == WEIGHT_MAX) return {}; auto R(this->reverse()); reset_visited_flag(end_node); visited[end_node] = true; int cu = end_node; std::vector> route; while (cu != start_node) { for (auto &e : R[cu]) { int to = e->opp(cu); if (visited[to]) continue; if (dist[cu] - e->cost == dist[to]) { visited[cu = to] = true; route.push_back(e->reverse()); break; } } } std::reverse(route.begin(), route.end()); return route; } Weight diameter() const { static_assert(!is_directed); assert(is_tree()); std::vector dist(distances(0, -1)); dist = distances( std::max_element(dist.begin(), dist.end()) - dist.begin(), -1); return *std::max_element(dist.begin(), dist.end()); } graph reverse() const { if constexpr (!is_directed) { return *this; } else { graph ret(this->N); for (auto &e : this->E) { ret.add_edge(e->reverse()); } return ret; } } /** * @brief 行きがけ順に dfs */ std::vector preorder(int start) const { std::vector result; reset_visited_flag(start); visited[start] = true; auto dfs = [&](auto self, int cu) -> void { result.push_back(cu); for (auto &e : this->G[cu]) { int to = e->opp(cu); if (visited[to]) continue; visited[to] = true; self(self, to); } }; dfs(dfs, start); return result; } /** * @brief 通りがけ順に dfs */ std::vector inorder(int start) const { std::vector result; reset_visited_flag(start); visited[start] = true; auto dfs = [&](auto self, int cu) -> void { for (auto &e : this->G[cu]) { int to = e->opp(cu); if (visited[to]) continue; visited[to] = true; result.push_back(cu); self(self, to); } result.push_back(cu); }; dfs(dfs, start); return result; } /** * @brief 帰りがけ順に dfs */ std::vector postorder(int start) const { std::vector result; reset_visited_flag(start); visited[start] = true; auto dfs = [&](auto self, int cu) -> void { for (auto &e : this->G[cu]) { int to = e->opp(cu); if (visited[to]) continue; visited[to] = true; self(self, to); } result.push_back(cu); }; dfs(dfs, start); return result; } std::vector topological_sort() { static_assert(is_directed); std::vector indeg(this->N, 0), sorted; for (auto &e : this->E) { indeg[e->v[1]]++; } std::queue q; for (int i = 0; i < this->N; i++) if (!indeg[i]) q.push(i); while (!q.empty()) { int cu = q.front(); q.pop(); for (auto e : this->G[cu]) { int to = e->opp(cu); if (!--indeg[to]) q.push(to); } sorted.push_back(cu); } return sorted; } /** * @return 最小全域森 */ graph minimum_spanning_forest() const { static_assert(!is_directed); graph ret(this->N); std::vector> tmp; for (auto &e : this->E) { tmp.emplace_back(*e); } std::sort(tmp.begin(), tmp.end(), [](const edge &a, const edge &b) { if (a.cost == b.cost) { if (a.v[0] == b.v[0]) { return a.v[1] < b.v[1]; } return a.v[0] < b.v[0]; } return a.cost < b.cost; }); for (auto &e : tmp) if (!ret.are_connected(e.v[0], e.v[1])) ret.add_edge(e); return ret; } private: /** * @see https://ei1333.github.io/luzhiled/snippets/graph/lowlink.html * @attention 非連結でも動作 */ int run_lowlink(int idx, int k, int par, std::vector &ord, std::vector &low, std::vector> &brds, std::vector &apts) { visited[idx] = true; ord[idx] = k++; low[idx] = ord[idx]; bool is_apt = false; int cnt = 0; for (auto &e : this->G[idx]) { int to = e->opp(idx); if (!visited[to]) { ++cnt; k = run_lowlink(to, k, idx, ord, low, brds, apts); low[idx] = std::min(low[idx], low[to]); is_apt |= ~par && low[to] >= ord[idx]; if (ord[idx] < low[to]) { brds.emplace_back(*e); } } else if (to != par) { low[idx] = std::min(low[idx], ord[to]); } } is_apt |= par == -1 && cnt > 1; if (is_apt) apts.push_back(idx); return k; } public: /** * @return pair, vector<関節点>> */ std::pair>, std::vector> lowlink() { static_assert(!is_directed); std::vector> brds; std::vector apts, ord(this->N, 0), low(this->N, 0); reset_visited_flag(); int k = 0; for (int i = 0; i < this->N; i++) { if (!visited[i]) k = run_lowlink(i, k, -1, ord, low, brds, apts); } return {brds, apts}; } // verify: https://atcoder.jp/contests/abc232/submissions/45715440 // 同型判定 bool operator==(const graph &other) const { if (this->N != other.count_nodes()) return false; if (this->count_edges() != other.count_edges()) return false; if (this->count_connected_components() != other.count_connected_components()) return false; matrix adj1(to_adjajency()), adj2(other.to_adjajency()); std::vector nodes_id(this->N); std::iota(nodes_id.begin(), nodes_id.end(), 0); do { bool ok = true; for (int i = 0; i < this->N; i++) for (int j = 0; j < this->N; j++) { if (adj1(i, j) != adj2(nodes_id[i], nodes_id[j])) { ok = false; break; } } if (ok) return true; } while (std::next_permutation(nodes_id.begin(), nodes_id.end())); return false; } bool operator!=(const graph &other) const { return !operator==(other); } }; #line 8 "library/gandalfr/other/io_supporter.hpp" #line 1 "library/atcoder/modint.hpp" #line 6 "library/atcoder/modint.hpp" #include #ifdef _MSC_VER #include #endif #line 1 "library/atcoder/internal_math.hpp" #line 5 "library/atcoder/internal_math.hpp" #ifdef _MSC_VER #include #endif namespace atcoder { namespace internal { // @param m `1 <= m` // @return x mod m constexpr long long safe_mod(long long x, long long m) { x %= m; if (x < 0) x += m; return x; } // Fast modular multiplication by barrett reduction // Reference: https://en.wikipedia.org/wiki/Barrett_reduction // NOTE: reconsider after Ice Lake struct barrett { unsigned int _m; unsigned long long im; // @param m `1 <= m < 2^31` explicit barrett(unsigned int m) : _m(m), im((unsigned long long)(-1) / m + 1) {} // @return m unsigned int umod() const { return _m; } // @param a `0 <= a < m` // @param b `0 <= b < m` // @return `a * b % m` unsigned int mul(unsigned int a, unsigned int b) const { // [1] m = 1 // a = b = im = 0, so okay // [2] m >= 2 // im = ceil(2^64 / m) // -> im * m = 2^64 + r (0 <= r < m) // let z = a*b = c*m + d (0 <= c, d < m) // a*b * im = (c*m + d) * im = c*(im*m) + d*im = c*2^64 + c*r + d*im // c*r + d*im < m * m + m * im < m * m + 2^64 + m <= 2^64 + m * (m + 1) < 2^64 * 2 // ((ab * im) >> 64) == c or c + 1 unsigned long long z = a; z *= b; #ifdef _MSC_VER unsigned long long x; _umul128(z, im, &x); #else unsigned long long x = (unsigned long long)(((unsigned __int128)(z)*im) >> 64); #endif unsigned int v = (unsigned int)(z - x * _m); if (_m <= v) v += _m; return v; } }; // @param n `0 <= n` // @param m `1 <= m` // @return `(x ** n) % m` constexpr long long pow_mod_constexpr(long long x, long long n, int m) { if (m == 1) return 0; unsigned int _m = (unsigned int)(m); unsigned long long r = 1; unsigned long long y = safe_mod(x, m); while (n) { if (n & 1) r = (r * y) % _m; y = (y * y) % _m; n >>= 1; } return r; } // Reference: // M. Forisek and J. Jancina, // Fast Primality Testing for Integers That Fit into a Machine Word // @param n `0 <= n` constexpr bool is_prime_constexpr(int n) { if (n <= 1) return false; if (n == 2 || n == 7 || n == 61) return true; if (n % 2 == 0) return false; long long d = n - 1; while (d % 2 == 0) d /= 2; constexpr long long bases[3] = {2, 7, 61}; for (long long a : bases) { long long t = d; long long y = pow_mod_constexpr(a, t, n); while (t != n - 1 && y != 1 && y != n - 1) { y = y * y % n; t <<= 1; } if (y != n - 1 && t % 2 == 0) { return false; } } return true; } template constexpr bool is_prime = is_prime_constexpr(n); // @param b `1 <= b` // @return pair(g, x) s.t. g = gcd(a, b), xa = g (mod b), 0 <= x < b/g constexpr std::pair inv_gcd(long long a, long long b) { a = safe_mod(a, b); if (a == 0) return {b, 0}; // Contracts: // [1] s - m0 * a = 0 (mod b) // [2] t - m1 * a = 0 (mod b) // [3] s * |m1| + t * |m0| <= b long long s = b, t = a; long long m0 = 0, m1 = 1; while (t) { long long u = s / t; s -= t * u; m0 -= m1 * u; // |m1 * u| <= |m1| * s <= b // [3]: // (s - t * u) * |m1| + t * |m0 - m1 * u| // <= s * |m1| - t * u * |m1| + t * (|m0| + |m1| * u) // = s * |m1| + t * |m0| <= b auto tmp = s; s = t; t = tmp; tmp = m0; m0 = m1; m1 = tmp; } // by [3]: |m0| <= b/g // by g != b: |m0| < b/g if (m0 < 0) m0 += b / s; return {s, m0}; } // Compile time primitive root // @param m must be prime // @return primitive root (and minimum in now) constexpr int primitive_root_constexpr(int m) { if (m == 2) return 1; if (m == 167772161) return 3; if (m == 469762049) return 3; if (m == 754974721) return 11; if (m == 998244353) return 3; int divs[20] = {}; divs[0] = 2; int cnt = 1; int x = (m - 1) / 2; while (x % 2 == 0) x /= 2; for (int i = 3; (long long)(i)*i <= x; i += 2) { if (x % i == 0) { divs[cnt++] = i; while (x % i == 0) { x /= i; } } } if (x > 1) { divs[cnt++] = x; } for (int g = 2;; g++) { bool ok = true; for (int i = 0; i < cnt; i++) { if (pow_mod_constexpr(g, (m - 1) / divs[i], m) == 1) { ok = false; break; } } if (ok) return g; } } template constexpr int primitive_root = primitive_root_constexpr(m); // @param n `n < 2^32` // @param m `1 <= m < 2^32` // @return sum_{i=0}^{n-1} floor((ai + b) / m) (mod 2^64) unsigned long long floor_sum_unsigned(unsigned long long n, unsigned long long m, unsigned long long a, unsigned long long b) { unsigned long long ans = 0; while (true) { if (a >= m) { ans += n * (n - 1) / 2 * (a / m); a %= m; } if (b >= m) { ans += n * (b / m); b %= m; } unsigned long long y_max = a * n + b; if (y_max < m) break; // y_max < m * (n + 1) // floor(y_max / m) <= n n = (unsigned long long)(y_max / m); b = (unsigned long long)(y_max % m); std::swap(m, a); } return ans; } } // namespace internal } // namespace atcoder #line 1 "library/atcoder/internal_type_traits.hpp" #line 7 "library/atcoder/internal_type_traits.hpp" namespace atcoder { namespace internal { #ifndef _MSC_VER template using is_signed_int128 = typename std::conditional::value || std::is_same::value, std::true_type, std::false_type>::type; template using is_unsigned_int128 = typename std::conditional::value || std::is_same::value, std::true_type, std::false_type>::type; template using make_unsigned_int128 = typename std::conditional::value, __uint128_t, unsigned __int128>; template using is_integral = typename std::conditional::value || is_signed_int128::value || is_unsigned_int128::value, std::true_type, std::false_type>::type; template using is_signed_int = typename std::conditional<(is_integral::value && std::is_signed::value) || is_signed_int128::value, std::true_type, std::false_type>::type; template using is_unsigned_int = typename std::conditional<(is_integral::value && std::is_unsigned::value) || is_unsigned_int128::value, std::true_type, std::false_type>::type; template using to_unsigned = typename std::conditional< is_signed_int128::value, make_unsigned_int128, typename std::conditional::value, std::make_unsigned, std::common_type>::type>::type; #else template using is_integral = typename std::is_integral; template using is_signed_int = typename std::conditional::value && std::is_signed::value, std::true_type, std::false_type>::type; template using is_unsigned_int = typename std::conditional::value && std::is_unsigned::value, std::true_type, std::false_type>::type; template using to_unsigned = typename std::conditional::value, std::make_unsigned, std::common_type>::type; #endif template using is_signed_int_t = std::enable_if_t::value>; template using is_unsigned_int_t = std::enable_if_t::value>; template using to_unsigned_t = typename to_unsigned::type; } // namespace internal } // namespace atcoder #line 14 "library/atcoder/modint.hpp" namespace atcoder { namespace internal { struct modint_base {}; struct static_modint_base : modint_base {}; template using is_modint = std::is_base_of; template using is_modint_t = std::enable_if_t::value>; } // namespace internal template * = nullptr> struct static_modint : internal::static_modint_base { using mint = static_modint; public: static constexpr int mod() { return m; } static mint raw(int v) { mint x; x._v = v; return x; } static_modint() : _v(0) {} template * = nullptr> static_modint(T v) { long long x = (long long)(v % (long long)(umod())); if (x < 0) x += umod(); _v = (unsigned int)(x); } template * = nullptr> static_modint(T v) { _v = (unsigned int)(v % umod()); } unsigned int val() const { return _v; } mint& operator++() { _v++; if (_v == umod()) _v = 0; return *this; } mint& operator--() { if (_v == 0) _v = umod(); _v--; return *this; } mint operator++(int) { mint result = *this; ++*this; return result; } mint operator--(int) { mint result = *this; --*this; return result; } mint& operator+=(const mint& rhs) { _v += rhs._v; if (_v >= umod()) _v -= umod(); return *this; } mint& operator-=(const mint& rhs) { _v -= rhs._v; if (_v >= umod()) _v += umod(); return *this; } mint& operator*=(const mint& rhs) { unsigned long long z = _v; z *= rhs._v; _v = (unsigned int)(z % umod()); return *this; } mint& operator/=(const mint& rhs) { return *this = *this * rhs.inv(); } mint operator+() const { return *this; } mint operator-() const { return mint() - *this; } mint pow(long long n) const { assert(0 <= n); mint x = *this, r = 1; while (n) { if (n & 1) r *= x; x *= x; n >>= 1; } return r; } mint inv() const { if (prime) { assert(_v); return pow(umod() - 2); } else { auto eg = internal::inv_gcd(_v, m); assert(eg.first == 1); return eg.second; } } friend mint operator+(const mint& lhs, const mint& rhs) { return mint(lhs) += rhs; } friend mint operator-(const mint& lhs, const mint& rhs) { return mint(lhs) -= rhs; } friend mint operator*(const mint& lhs, const mint& rhs) { return mint(lhs) *= rhs; } friend mint operator/(const mint& lhs, const mint& rhs) { return mint(lhs) /= rhs; } friend bool operator==(const mint& lhs, const mint& rhs) { return lhs._v == rhs._v; } friend bool operator!=(const mint& lhs, const mint& rhs) { return lhs._v != rhs._v; } private: unsigned int _v; static constexpr unsigned int umod() { return m; } static constexpr bool prime = internal::is_prime; }; template struct dynamic_modint : internal::modint_base { using mint = dynamic_modint; public: static int mod() { return (int)(bt.umod()); } static void set_mod(int m) { assert(1 <= m); bt = internal::barrett(m); } static mint raw(int v) { mint x; x._v = v; return x; } dynamic_modint() : _v(0) {} template * = nullptr> dynamic_modint(T v) { long long x = (long long)(v % (long long)(mod())); if (x < 0) x += mod(); _v = (unsigned int)(x); } template * = nullptr> dynamic_modint(T v) { _v = (unsigned int)(v % mod()); } unsigned int val() const { return _v; } mint& operator++() { _v++; if (_v == umod()) _v = 0; return *this; } mint& operator--() { if (_v == 0) _v = umod(); _v--; return *this; } mint operator++(int) { mint result = *this; ++*this; return result; } mint operator--(int) { mint result = *this; --*this; return result; } mint& operator+=(const mint& rhs) { _v += rhs._v; if (_v >= umod()) _v -= umod(); return *this; } mint& operator-=(const mint& rhs) { _v += mod() - rhs._v; if (_v >= umod()) _v -= umod(); return *this; } mint& operator*=(const mint& rhs) { _v = bt.mul(_v, rhs._v); return *this; } mint& operator/=(const mint& rhs) { return *this = *this * rhs.inv(); } mint operator+() const { return *this; } mint operator-() const { return mint() - *this; } mint pow(long long n) const { assert(0 <= n); mint x = *this, r = 1; while (n) { if (n & 1) r *= x; x *= x; n >>= 1; } return r; } mint inv() const { auto eg = internal::inv_gcd(_v, mod()); assert(eg.first == 1); return eg.second; } friend mint operator+(const mint& lhs, const mint& rhs) { return mint(lhs) += rhs; } friend mint operator-(const mint& lhs, const mint& rhs) { return mint(lhs) -= rhs; } friend mint operator*(const mint& lhs, const mint& rhs) { return mint(lhs) *= rhs; } friend mint operator/(const mint& lhs, const mint& rhs) { return mint(lhs) /= rhs; } friend bool operator==(const mint& lhs, const mint& rhs) { return lhs._v == rhs._v; } friend bool operator!=(const mint& lhs, const mint& rhs) { return lhs._v != rhs._v; } private: unsigned int _v; static internal::barrett bt; static unsigned int umod() { return bt.umod(); } }; template internal::barrett dynamic_modint::bt(998244353); using modint998244353 = static_modint<998244353>; using modint1000000007 = static_modint<1000000007>; using modint = dynamic_modint<-1>; namespace internal { template using is_static_modint = std::is_base_of; template using is_static_modint_t = std::enable_if_t::value>; template struct is_dynamic_modint : public std::false_type {}; template struct is_dynamic_modint> : public std::true_type {}; template using is_dynamic_modint_t = std::enable_if_t::value>; } // namespace internal } // namespace atcoder #line 10 "library/gandalfr/other/io_supporter.hpp" template std::ostream &operator<<(std::ostream &os, const std::vector &v) { for (int i = 0; i < (int)v.size(); i++) os << v[i] << (i + 1 != (int)v.size() ? " " : ""); return os; } template std::ostream &operator<<(std::ostream &os, const std::set &st) { for (const T &x : st) { std::cout << x << " "; } return os; } template std::ostream &operator<<(std::ostream &os, const std::multiset &st) { for (const T &x : st) { std::cout << x << " "; } return os; } template std::ostream &operator<<(std::ostream &os, const std::deque &dq) { for (const T &x : dq) { std::cout << x << " "; } return os; } template std::ostream &operator<<(std::ostream &os, const std::pair &p) { os << p.first << ' ' << p.second; return os; } template std::ostream &operator<<(std::ostream &os, std::queue &q) { int sz = q.size(); while (--sz) { os << q.front() << ' '; q.push(q.front()); q.pop(); } os << q.front(); q.push(q.front()); q.pop(); return os; } namespace atcoder { template std::ostream &operator<<(std::ostream &os, const static_modint &mi) { os << mi.val(); return os; } template std::ostream &operator<<(std::ostream &os, const dynamic_modint &mi) { os << mi.val(); return os; } } template std::istream &operator>>(std::istream &is, std::vector &v) { for (T &in : v) is >> in; return is; } template std::istream &operator>>(std::istream &is, std::pair &p) { is >> p.first >> p.second; return is; } namespace atcoder { template std::istream &operator>>(std::istream &is, static_modint &mi) { long long n; is >> n; mi = n; return is; } template std::istream &operator>>(std::istream &is, dynamic_modint &mi) { long long n; is >> n; mi = n; return is; } } #line 4 "playspace/main.cpp" using namespace std; using ll = long long; const int INF = 1001001001; const ll INFLL = 1001001001001001001; const ll MOD = 1000000007; const ll _MOD = 998244353; #define rep(i, j, n) for(ll i = (ll)(j); i < (ll)(n); i++) #define rrep(i, j, n) for(ll i = (ll)(n-1); i >= (ll)(j); i--) #define all(a) (a).begin(),(a).end() #define debug(a) std::cerr << #a << ": " << a << std::endl #define LF cout << endl template inline bool chmax(T1 &a, T2 b) { return a < b && (a = b, true); } template inline bool chmin(T1 &a, T2 b) { return a > b && (a = b, true); } void Yes(bool ok){ std::cout << (ok ? "Yes" : "No") << std::endl; } int main(void){ string S, T; cin >> S >> T; int a = S.back() - '0'; int b = T.back() - '0'; cout << ((a - b) % 2 == 0 ? "Possible" : "Impossible") << endl; }