結果
| 問題 |
No.2525 Great Move
|
| コンテスト | |
| ユーザー |
Gandalfr
|
| 提出日時 | 2023-11-03 21:22:26 |
| 言語 | C++17 (gcc 13.3.0 + boost 1.87.0) |
| 結果 |
AC
|
| 実行時間 | 10 ms / 2,000 ms |
| コード長 | 50,158 bytes |
| コンパイル時間 | 2,029 ms |
| コンパイル使用メモリ | 199,020 KB |
| 最終ジャッジ日時 | 2025-02-17 17:27:39 |
|
ジャッジサーバーID (参考情報) |
judge2 / judge2 |
(要ログイン)
| ファイルパターン | 結果 |
|---|---|
| sample | AC * 3 |
| other | AC * 26 |
ソースコード
#line 1 "playspace/main.cpp"
#include <bits/stdc++.h>
#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<int> par;
std::vector<int> 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<int> group_containing_node(int x) const {
std::vector<int> 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<std::vector<int>> all_groups() const {
std::vector<std::vector<int>> result;
result.reserve(group_num);
std::vector<bool> 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 T> class matrix {
private:
int H, W;
std::valarray<std::valarray<T>> table;
enum rowtrans_operation_name { SCALE, SWAP, ADD };
struct rowtrans_operation {
int op, tar, res;
T scl;
};
using operations_history = std::vector<rowtrans_operation>;
public:
matrix() = default;
matrix(int _H, int _W, T val = 0)
: H(_H), W(_W), table(std::valarray<T>(val, _W), _H) {}
matrix(const std::vector<std::vector<T>> &vv)
: H(vv.size()), W(vv[0].size()), table(std::valarray<T>(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<std::valarray<T>> &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<T>(val, _H));
}
int size_H() const { return H; }
int size_W() const { return W; }
void transpose() {
matrix<T> 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<T> &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<T> 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<T> solve_system_of_equations(const std::vector<T> &y) {
assert(H == W);
std::vector<T> x(y);
matrix<T> 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<T> inverse() const {
assert(H == W);
matrix<T> INV(matrix<T>::E(H)), U(*this);
auto hist = U.sweep_method();
if (U.table[H - 1][H - 1] == 0)
return matrix<T>(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<T> &operator+=(const matrix<T> &a) {
this->table += a.table;
return *this;
}
matrix<T> &operator-=(const matrix<T> &a) {
this->table -= a.table;
return *this;
}
matrix<T> &operator*=(const T &a) {
this->table *= a;
return *this;
}
matrix<T> &operator*=(const matrix<T> &a) {
assert(W == a.H);
matrix<T> 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<T> &operator/=(const T &a) {
this->table /= a;
return *this;
}
/**
* @brief 行列の冪乗。
* @param n 指数
* @attention n が 0 なら単位行列。
* @attention 演算子の優先度に注意。
*/
matrix<T> operator^=(long long n) {
assert(H == W);
if (n == 0)
return *this = E(H);
n--;
matrix<T> x(*this);
while (n) {
if (n & 1)
*this *= x;
x *= x;
n >>= 1;
}
return *this;
}
matrix<T> operator+() const { return *this; }
matrix<T> operator-() const { return matrix<T>(*this) *= -1; }
matrix<T> operator+(const matrix<T> &a) const {
return matrix<T>(*this) += a;
}
matrix<T> operator-(const matrix<T> &a) const {
return matrix<T>(*this) -= a;
}
matrix<T> operator*(const T &a) { return matrix<T>(*this) *= a; }
matrix<T> operator*(const matrix<T> &a) const {
return matrix<T>(*this) *= a;
}
matrix<T> operator/(const T &a) const { return matrix<T>(*this) /= a; }
matrix<T> operator^(long long n) const { return matrix<T>(*this) ^= n; }
friend std::istream &operator>>(std::istream &is, matrix<T> &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 <typename S> bool operator==(const matrix<S> &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 <typename S> bool operator!=(const matrix<S> &other) {
return !operator==(other);
}
/**
* @brief サイズ n の単位行列。
*/
static matrix<T> E(int N) {
matrix<T> 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 <class Weight> 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<Weight> &e) {
e.print(os);
return os;
}
protected:
virtual void print(std::ostream &os) const = 0;
};
} // namespace internal
template <class Weight> struct edge : public internal::_base_edge<Weight> {
using internal::_base_edge<Weight>::_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<int> : public internal::_base_edge<int> {
static inline const int cost = 1;
using internal::_base_edge<int>::_base_edge;
edge(int _from, int _to, int _id) : _base_edge<int>(_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 <class Flow, class Cost>
struct flow_edge : public internal::_base_edge<Cost> {
private:
Flow res, cap;
using internal::_base_edge<Cost>::cost;
public:
flow_edge() {}
flow_edge(int from, int to, Flow res, Flow cap, int id)
: internal::_base_edge<Cost>(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<Cost>(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 <typename Edge> class _base_graph {
protected:
int N;
std::vector<std::vector<Edge *>> G;
std::vector<std::unique_ptr<Edge>> 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<Edge *> &operator[](int n) const { return G[n]; }
/**
* @return グラフ全体の辺のポインタのリストの const 参照
*/
const std::vector<std::unique_ptr<Edge>> &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 <typename Weight, bool is_directed>
class graph : public internal::_base_graph<edge<Weight>> {
private:
union_find uf;
mutable std::vector<bool> visited; // dfs / bfs のための領域
Weight W = 0;
bool forest_flag = true;
static inline const Weight WEIGHT_MAX = std::numeric_limits<Weight>::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<edge<Weight>>(n), uf(n), visited(n) {}
graph(int n, int m)
: internal::_base_graph<edge<Weight>>(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<Weight> &e) {
forest_flag &= uf.merge(e.v[0], e.v[1]);
this->E.emplace_back(std::make_unique<edge<Weight>>(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<Weight, int>::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<Weight, int>::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<std::vector<int>> weakly_connected_components() const {
return uf.all_groups();
}
/**
* @brief ノード x が含まれている弱連結成分のリストを返す
*/
std::vector<int> 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<graph>, std::vector<int>, std::vector<int>>
decompose() const {
std::vector<graph> Gs(uf.count_groups());
std::vector<std::vector<int>> groups(uf.all_groups());
std::vector<int> 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<Weight> to_adjajency(Weight invalid = 0) const {
matrix<Weight> 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<Weight, int>;
using Dijkstra_queue =
std::priority_queue<PAIR, std::vector<PAIR>, std::greater<PAIR>>;
void run_bfs(std::vector<int> &dist, std::queue<int> &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<Weight> &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<Weight> distances(int start_node, Weight invalid) const {
std::vector<Weight> dist(this->N, WEIGHT_MAX);
dist[start_node] = 0;
if constexpr (std::is_same<Weight, int>::value) {
// BFS algorithm
std::queue<int> 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<Weight> 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<edge<Weight>> 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<edge<Weight>> 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<Weight> 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<int> preorder(int start) const {
std::vector<int> 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<int> inorder(int start) const {
std::vector<int> 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<int> postorder(int start) const {
std::vector<int> 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<int> topological_sort() {
static_assert(is_directed);
std::vector<int> indeg(this->N, 0), sorted;
for (auto &e : this->E) {
indeg[e->v[1]]++;
}
std::queue<int> 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<edge<Weight>> tmp;
for (auto &e : this->E) {
tmp.emplace_back(*e);
}
std::sort(tmp.begin(), tmp.end(),
[](const edge<Weight> &a, const edge<Weight> &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<int> &ord,
std::vector<int> &low, std::vector<edge<Weight>> &brds,
std::vector<int> &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<橋>, vector<関節点>>
*/
std::pair<std::vector<edge<Weight>>, std::vector<int>> lowlink() {
static_assert(!is_directed);
std::vector<edge<Weight>> brds;
std::vector<int> 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<Weight> adj1(to_adjajency()), adj2(other.to_adjajency());
std::vector<int> 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 <type_traits>
#ifdef _MSC_VER
#include <intrin.h>
#endif
#line 1 "library/atcoder/internal_math.hpp"
#line 5 "library/atcoder/internal_math.hpp"
#ifdef _MSC_VER
#include <intrin.h>
#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 <int n> 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<long long, long long> 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 <int m> 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 <class T>
using is_signed_int128 =
typename std::conditional<std::is_same<T, __int128_t>::value ||
std::is_same<T, __int128>::value,
std::true_type,
std::false_type>::type;
template <class T>
using is_unsigned_int128 =
typename std::conditional<std::is_same<T, __uint128_t>::value ||
std::is_same<T, unsigned __int128>::value,
std::true_type,
std::false_type>::type;
template <class T>
using make_unsigned_int128 =
typename std::conditional<std::is_same<T, __int128_t>::value,
__uint128_t,
unsigned __int128>;
template <class T>
using is_integral = typename std::conditional<std::is_integral<T>::value ||
is_signed_int128<T>::value ||
is_unsigned_int128<T>::value,
std::true_type,
std::false_type>::type;
template <class T>
using is_signed_int = typename std::conditional<(is_integral<T>::value &&
std::is_signed<T>::value) ||
is_signed_int128<T>::value,
std::true_type,
std::false_type>::type;
template <class T>
using is_unsigned_int =
typename std::conditional<(is_integral<T>::value &&
std::is_unsigned<T>::value) ||
is_unsigned_int128<T>::value,
std::true_type,
std::false_type>::type;
template <class T>
using to_unsigned = typename std::conditional<
is_signed_int128<T>::value,
make_unsigned_int128<T>,
typename std::conditional<std::is_signed<T>::value,
std::make_unsigned<T>,
std::common_type<T>>::type>::type;
#else
template <class T> using is_integral = typename std::is_integral<T>;
template <class T>
using is_signed_int =
typename std::conditional<is_integral<T>::value && std::is_signed<T>::value,
std::true_type,
std::false_type>::type;
template <class T>
using is_unsigned_int =
typename std::conditional<is_integral<T>::value &&
std::is_unsigned<T>::value,
std::true_type,
std::false_type>::type;
template <class T>
using to_unsigned = typename std::conditional<is_signed_int<T>::value,
std::make_unsigned<T>,
std::common_type<T>>::type;
#endif
template <class T>
using is_signed_int_t = std::enable_if_t<is_signed_int<T>::value>;
template <class T>
using is_unsigned_int_t = std::enable_if_t<is_unsigned_int<T>::value>;
template <class T> using to_unsigned_t = typename to_unsigned<T>::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 <class T> using is_modint = std::is_base_of<modint_base, T>;
template <class T> using is_modint_t = std::enable_if_t<is_modint<T>::value>;
} // namespace internal
template <int m, std::enable_if_t<(1 <= m)>* = 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 <class T, internal::is_signed_int_t<T>* = nullptr>
static_modint(T v) {
long long x = (long long)(v % (long long)(umod()));
if (x < 0) x += umod();
_v = (unsigned int)(x);
}
template <class T, internal::is_unsigned_int_t<T>* = 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<m>;
};
template <int id> 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 <class T, internal::is_signed_int_t<T>* = nullptr>
dynamic_modint(T v) {
long long x = (long long)(v % (long long)(mod()));
if (x < 0) x += mod();
_v = (unsigned int)(x);
}
template <class T, internal::is_unsigned_int_t<T>* = 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 <int id> internal::barrett dynamic_modint<id>::bt(998244353);
using modint998244353 = static_modint<998244353>;
using modint1000000007 = static_modint<1000000007>;
using modint = dynamic_modint<-1>;
namespace internal {
template <class T>
using is_static_modint = std::is_base_of<internal::static_modint_base, T>;
template <class T>
using is_static_modint_t = std::enable_if_t<is_static_modint<T>::value>;
template <class> struct is_dynamic_modint : public std::false_type {};
template <int id>
struct is_dynamic_modint<dynamic_modint<id>> : public std::true_type {};
template <class T>
using is_dynamic_modint_t = std::enable_if_t<is_dynamic_modint<T>::value>;
} // namespace internal
} // namespace atcoder
#line 10 "library/gandalfr/other/io_supporter.hpp"
template <typename T>
std::ostream &operator<<(std::ostream &os, const std::vector<T> &v) {
for (int i = 0; i < (int)v.size(); i++)
os << v[i] << (i + 1 != (int)v.size() ? " " : "");
return os;
}
template <typename T>
std::ostream &operator<<(std::ostream &os, const std::set<T> &st) {
for (const T &x : st) {
std::cout << x << " ";
}
return os;
}
template <typename T>
std::ostream &operator<<(std::ostream &os, const std::multiset<T> &st) {
for (const T &x : st) {
std::cout << x << " ";
}
return os;
}
template <typename T>
std::ostream &operator<<(std::ostream &os, const std::deque<T> &dq) {
for (const T &x : dq) {
std::cout << x << " ";
}
return os;
}
template <typename T1, typename T2>
std::ostream &operator<<(std::ostream &os, const std::pair<T1, T2> &p) {
os << p.first << ' ' << p.second;
return os;
}
template <typename T>
std::ostream &operator<<(std::ostream &os, std::queue<T> &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 <int m>
std::ostream &operator<<(std::ostream &os, const static_modint<m> &mi) {
os << mi.val();
return os;
}
template <int m>
std::ostream &operator<<(std::ostream &os, const dynamic_modint<m> &mi) {
os << mi.val();
return os;
}
}
template <typename T>
std::istream &operator>>(std::istream &is, std::vector<T> &v) {
for (T &in : v)
is >> in;
return is;
}
template <typename T1, typename T2>
std::istream &operator>>(std::istream &is, std::pair<T1, T2> &p) {
is >> p.first >> p.second;
return is;
}
namespace atcoder {
template <int m>
std::istream &operator>>(std::istream &is, static_modint<m> &mi) {
long long n;
is >> n;
mi = n;
return is;
}
template <int m>
std::istream &operator>>(std::istream &is, dynamic_modint<m> &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<typename T1, typename T2> inline bool chmax(T1 &a, T2 b) { return a < b && (a = b, true); }
template<typename T1, typename T2> 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;
}
Gandalfr