/** * @file template.cpp * @brief 競技プログラミングのデッキ * @author Gex777 * @date update:2024/03/10 */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // #include //ACL using namespace std; // using namespace atcoder; // 独自型定義 using ll = long long; using ull = unsigned long long; using vi = vector; using vvi = vector; using vll = vector; using vvll = vector; using vvvll = vector; using vs = vector; using vc = vector; using vvc = vector; using vb = vector; using vvb = vector; using vd = vector; using vvd = vector; using ld = long double; using pii = pair; using pll = pair; // マクロの宣言 #define all(x) x.begin(), x.end() #define rall(x) x.rbegin(), e.rend() // 定数宣言 constexpr double PI = 3.141592653589793; constexpr ll MOD998 = 998244353; constexpr ll MOD107 = 1000000007; /**************************************************** *************ここから下は自作ライブラリ************** ****************************************************/ // コンテナの中身を表示, 1行で出力をする, debug用 template void printv(T &a) { for (const auto &x : a) { cout << x << " "; } puts(""); return; } // コンテナの中身を表示, 二次元配列用 template void printvv(T &a) { for (const auto &x : a) { printv(x); } } // コンテナの中身を表示, pair型 template void print_vpair(const T &a) { for (const auto &x : a) { cout << "(" << x.first << ", " << x.second << "), "; } puts(""); return; } template inline bool chmin(T &a, T b) { if (a > b) { a = b; return true; } return false; } template inline bool chmax(T &a, T b) { if (a < b) { a = b; return true; } return false; } // コンテナの中身を表示, 改行して出力をする, debug用 template void println(T &a) { for (const auto &x : a) { cout << x << endl; } return; } // 1~Nまでの総和を求める ll sum_from_1_to_N(ll N) { if (N < 1LL) { return 0; } if ((N & 1) == 0) // even { return N / 2 * (N + 1); } else // odd { return (N + 1) / 2 * N; } } // A+1~Nまでの総和を求める ll sum_from_A_to_B(ll A, ll B) { return sum_from_1_to_N(B) - sum_from_1_to_N(A); } // a^bを求める, 繰り返し2乗法を用いる,3番目の引数はModを取る場合に設定 ll intPowMod(ll a, ll b, const ll MOD = LLONG_MAX) { ll ans = 1; ll A = a; while (b > 0) { int n = b % 2; b /= 2; if (n == 1) { ans *= A % MOD; ans %= MOD; } A = ((A % MOD) * (A % MOD)) % MOD; } return ans; } ld arg_to_rad(ld arg) { return (arg * PI / 180.0); } ld rad_to_arg(ld rad) { return rad * 180.0 / PI; } // C(n, m)を求める ll combination(const ll n, const ll m) { assert(n >= m); // n>=mは保証される ll up = 1; ll down = 1; for (int i = n; i > n - m; i--) { up *= i; } for (int i = m; i >= 2; i--) { down *= i; } return up / down; } // 動的計画法で前処理,O(N**2) vvll combination_table(const int MAX_N = 50) { vvll com = vvll(MAX_N + 1, vll(MAX_N + 1, 0)); // 前計算の結果を保存 com[0][0] = 1; for (int i = 1; i <= MAX_N; ++i) { com[i][0] = 1; for (int j = 1; j <= MAX_N; j++) { com[i][j] = (com[i - 1][j - 1] + com[i - 1][j]); } } return com; } // a÷bをMODで割ったあまりを返す関数 ll DivisionMod(ll a, ll b, ll MOD) { return (a * intPowMod(b, MOD - 2, MOD)) % MOD; } // C(n, r)をModで割った余りを返す関数, 3番めの引数を入れないと通常のConbination ll combinationMod(ll n, ll r, ll MOD = LLONG_MAX) { // 分子upを求める ll up = 1; for (ll i = n - r + 1; i <= n; ++i) { up = (up * i) % MOD; } // 分母downを求める ll down = 1; for (ll i = 1; i <= r; ++i) down = (down * i) % MOD; return DivisionMod(up, down, MOD); } // a,bの最大公約数を求める, A>=B>=0の時, 計算量O(logB) long long GCD(long long a, long long b) { if (b == 0) return a; else return GCD(b, a % b); } // 拡張ユークリッドの互助法, 返り値: a と b の最大公約数 // ax + by = gcd(a, b) を満たす (x, y) が格納される long long extGCD(long long a, long long b, long long &x, long long &y) { if (b == 0) { x = 1; y = 0; return a; } long long d = extGCD(b, a % b, y, x); y -= a / b * x; return d; } // 最小公倍数 ll LCM(ll a, ll b) { return a * b / GCD(a, b); } // 素数判定, P->true, not_P->false bool check_Prime(ll N) { if (N == 2) return true; if (N == 1 || (N & 1) == 0) return false; for (ll i = 3; i <= sqrt(N); i += 2) { if (N % i == 0) return false; } return true; } // 素因数分解,key:素数, value:指数のmapを返す map prime_factorize(ll number) { if (number == 1) { return {{1LL, 1LL}}; } map table; for (ll i = 2; i * i <= number; ++i) { while (number % i == 0) { table[i]++; number /= i; } } if (number != 1LL) { table[number]++; } return table; } // Eratosthenesのtableを使った素因数分解 vector prime_factorize(ll number, const vi &IsPrime) { if (number == 1) { return {{1, 1}}; } vector factor; while (number != 1) { int cnt = 0; int f = IsPrime[number]; while (IsPrime[number] == f) { number /= IsPrime[number]; ++cnt; } factor.push_back({f, cnt}); } return factor; } /* エラストステネス and 高速素因数分解 prime->IsPrime[i]=i; not prime ->最小の素因数 高速素因数分解をするのにはprime_factorize(ll num, ll table)を使う */ vi Eratosthenes(size_t max_number) { vi IsPrime(max_number + 1); // tableの初期化 for (int i = 1; i < IsPrime.size(); ++i) { IsPrime[i] = i; } for (int i = 2; i <= sqrt(max_number); ++i) { for (int j = i; j <= max_number; j += i) { if (IsPrime[j] == j) { IsPrime[j] = i; } } } return IsPrime; } // O(N)でNの階乗を求める ll factorial(const ll N) { ll ans = 1; for (ll i = 1; i <= N; ++i) { ans *= i; } return ans; } // Run Length Encoding, ランレングス圧縮 template vector> RLE(const vector &A) { vector> rle; rle.push_back({A.front(), 1}); for (int i = 1; i < A.size(); ++i) { if (rle.back().first == A[i]) { rle.back().second++; } else { rle.push_back({A[i], 1}); } } return rle; } vector> RLE(const string &S) { vector> rle; rle.push_back({S.front(), 1}); for (int i = 1; i < S.size(); ++i) { if (rle.back().first == S[i]) { rle.back().second++; } else { rle.push_back({S[i], 1}); } } return rle; } void DEBUG_INDICATE() { static int cnt = 0; cout << "-----DEBUG:" << cnt++ << "------" << endl; return; } // 正方行列の回転を行うライブラリ template struct ROTATE { static vector> clockwise(const vector> &X) { assert(X.size() == X[0].size()); const int N = X.size(); vector> Y(N, vector(N)); for (int i = 0; i < N; ++i) for (int j = 0; j < N; ++j) Y[j][N - 1 - i] = X[i][j]; return Y; } static vector> anticlockwise(const vector> &X) { assert(X.size() == X[0].size()); const int N = X.size(); vector> Y(N, vector(N)); for (int i = 0; i < N; ++i) for (int j = N - 1; 0 <= j; --j) Y[N - 1 - j][i] = X[i][j]; return Y; } }; /* RMQ:[0,n-1] について、区間ごとの最小値を管理する構造体 update(i,x): i 番目の要素を x に更新。O(log(n)) query(a,b): [a,b) での最小の要素を取得。O(log(n)) */ template struct RangeMinimumQuery { const T INF = numeric_limits::max(); int n; // 葉の数 vector dat; // 完全二分木の配列 RangeMinimumQuery(int n_) : n(), dat(n_ * 4, INF) { // 葉の数は 2^x の形 int x = 1; while (n_ > x) { x *= 2; } n = x; } void update(int i, T x) { i += n - 1; dat[i] = x; while (i > 0) { i = (i - 1) / 2; // parent dat[i] = min(dat[i * 2 + 1], dat[i * 2 + 2]); } } // the minimum element of [a,b) T query(int a, int b) { return query_sub(a, b, 0, 0, n); } T query_sub(int a, int b, int k, int l, int r) { if (r <= a || b <= l) { return INF; } else if (a <= l && r <= b) { return dat[k]; } else { T vl = query_sub(a, b, k * 2 + 1, l, (l + r) / 2); T vr = query_sub(a, b, k * 2 + 2, (l + r) / 2, r); return min(vl, vr); } } }; // 1次元累積和のためのクラス template struct CSUM1D { vector csum; // 1次元累積和にするコンストラクタ CSUM1D(const vector &_csum) { const size_t N = _csum.size(); csum = vll(N + 1, 0); for (int i = 0; i < N; ++i) csum[i + 1] = csum[i] + _csum[i]; } // 区間[L,R]の合計値(閉区間) T get(const int L, const int R) { const size_t N = csum.size(); assert(1 <= L && L <= R && R <= N); return csum[R] - csum[L-1]; } }; // 二次元累積和のためのクラス template struct CSUM2D { vector> csum; // 二次元累積和にするコンストラクタ CSUM2D(const vector> &_csum) { const int N = _csum.size(); const int M = _csum[0].size(); csum = vector>(N + 1, vector(M + 1, 0)); for (int i = 1; i <= N; ++i) for (int j = 1; j <= M; ++j) csum[i][j] = _csum[i - 1][j - 1]; for (int i = 0; i <= N; ++i) for (int j = 0; j < M; ++j) csum[i][j + 1] = csum[i][j] + csum[i][j + 1]; for (int j = 0; j <= M; ++j) for (int i = 0; i < N; ++i) csum[i + 1][j] = csum[i][j] + csum[i + 1][j]; } //(i1,j1)を左上,(i2,j2)を右下とする長方形領域の合計値(閉区間) T get(const int i1, const int j1, const int i2, const int j2) { assert(i1 <= i2 && j1 <= j2); assert(1 <= i1 && i1 < csum.size() && 1 <= j1 && j1 < csum[0].size()); assert(1 <= i2 && i2 < csum.size() && 1 <= j2 && j2 < csum[0].size()); T s4 = csum[i2][j2]; T s1 = csum[i1 - 1][j1 - 1]; T s2 = csum[i1 - 1][j2]; T s3 = csum[i2][j1 - 1]; return s4 - s3 - s2 + s1; } }; /** * @brief 要素数N,二項演算op, 初期値INFを指定するセグ木 * update(i,x): i 番目の要素を x に更新。O(log(n)) * query(a,b): [a,b) での最小の要素を取得。O(log(n)) */ template struct SegTree { T INF; // 初期値 int N; // 葉の数 vector data; // 完全二分木の配列 function op; // 二項演算 SegTree(int _N, const function _op, const T _INF) : N(), data(_N * 4, _INF), op(_op), INF(_INF) { // 葉の数は 2^x の形 int x = 1; while (_N > x) { x *= 2; } N = x; } void update(int i, T x) { i += N - 1; data[i] = x; while (i > 0) { i = (i - 1) / 2; // parent data[i] = op(data[i * 2 + 1], data[i * 2 + 2]); } } // the minimum element of [a,b) T query(int a, int b) { return query_sub(a, b, 0, 0, N); } T query_sub(int a, int b, int k, int l, int r) { if (r <= a || b <= l) { return INF; } else if (a <= l && r <= b) { return data[k]; } else { T vl = query_sub(a, b, k * 2 + 1, l, (l + r) / 2); T vr = query_sub(a, b, k * 2 + 2, (l + r) / 2, r); return op(vl, vr); } } // 添字でアクセス T operator[](int i) { return data[i + N - 1]; } }; // Union-Find struct UnionFind { vector par, rank, siz; // 構造体の初期化 UnionFind(int n) : par(n, -1), rank(n, 0), siz(n, 1) {} // 根を求める int root(int x) { if (par[x] == -1) return x; // x が根の場合は x を返す else return par[x] = root(par[x]); // 経路圧縮 } // x と y が同じグループに属するか (= 根が一致するか) bool issame(int x, int y) { return root(x) == root(y); } // x を含むグループと y を含むグループを併合する bool unite(int x, int y) { int rx = root(x), ry = root(y); // x 側と y 側の根を取得する if (rx == ry) return false; // すでに同じグループのときは何もしない // union by rank if (rank[rx] < rank[ry]) swap(rx, ry); // ry 側の rank が小さくなるようにする par[ry] = rx; // ry を rx の子とする if (rank[rx] == rank[ry]) rank[rx]++; // rx 側の rank を調整する siz[rx] += siz[ry]; // rx 側の siz を調整する return true; } // x を含む根付き木のサイズを求める int size(int x) { return siz[root(x)]; } }; /********************************************* *************ライブラリ終わり****************** ***********************************************/ int main() { int K; cin >> K; vs NUPC; for(int i=0; i<(1<<4); ++i) { string temp; for(int bit=3; 0<=bit; --bit) { temp += (i & (1<