結果
問題 | No.442 和と積 |
ユーザー |
|
提出日時 | 2025-04-28 15:50:49 |
言語 | C++23 (gcc 13.3.0 + boost 1.87.0) |
結果 |
AC
|
実行時間 | 2 ms / 1,000 ms |
コード長 | 17,365 bytes |
コンパイル時間 | 4,997 ms |
コンパイル使用メモリ | 299,992 KB |
実行使用メモリ | 6,272 KB |
最終ジャッジ日時 | 2025-04-28 15:50:55 |
合計ジャッジ時間 | 5,461 ms |
ジャッジサーバーID (参考情報) |
judge2 / judge3 |
(要ログイン)
ファイルパターン | 結果 |
---|---|
sample | AC * 3 |
other | AC * 18 |
ソースコード
#include <bits/stdc++.h> using namespace std; typedef long long ll; typedef std::pair<long long, long long> P; typedef std::priority_queue<P, std::vector<P>, std::greater<P>> PQ; typedef std::complex<double> cd; struct P3 { long long first, second, third; }; struct P4 { long long first, second, third, fourth; }; struct P3P { P first, second, third; }; struct compP3{ bool operator()(const P3 &p1,const P3 &p2) const { if (p1.first != p2.first) return p1.first < p2.first; if (p1.second != p2.second) return p1.second < p2.second; else return p1.third < p2.third; } }; struct gcompP3{ bool operator()(const P3 &p1,const P3 &p2) const { if (p1.first != p2.first) return p1.first > p2.first; if (p1.second != p2.second) return p1.second > p2.second; else return p1.third > p2.third; } }; struct comp{ bool operator()(const P3P &p1,const P3P &p2) const { ll a = p1.first.first + p1.second.first + p1.third.first; ll b = p2.first.first + p2.second.first + p2.third.first; return a < b; } }; const double PI = acos(-1.0); bool ckran(int a, int n) { return (a >= 0 && a < n); } void yn (bool f) { if (f) std::cout << "Yes" << '\n'; else std::cout << "No" << '\n'; } void zo (bool f) { if (f) std::cout << 1 << '\n'; else std::cout << 0 << '\n'; } long long sclog10(long long n) { long long ans = 0; while (n != 0) { n /= 10; ans++; } return ans; } long long longpow(long long x, long long n) { long long ans = 1; for (long long i = 0; i < n; ++i) { ans *= x; } return ans; } long long pplus(P a) { return a.first + a.second; } long long pminus(P a) { return a.first - a.second; } long long ptime(P a) { return a.first * a.second; } long long pdiv(P a) { return a.first / a.second; } template<typename T, typename U> bool chmax(T& a, U b) { if (a < b) { a = b; return true; } else { return false; } } template<typename T, typename U> bool chmin(T& a, U b) { if (a > b) { a = b; return true; } else { return false; } } template<typename T> void outspace(std::vector<T> a) { int n = a.size(); for (int i = 0; i < n; ++i) { std::cout << a[i]; if (i != n - 1) std::cout << " "; else std::cout << std::endl; } } void outspace(P a) { std::cout << a.first << ' ' << a.second << '\n'; } std::vector<long long> lltovec(long long n, long long base = 10, long long minsize = -1) { std::vector<long long> res; while (minsize-- > 0 || n > 0) { res.push_back(n % base); n /= base; } // std::reverse(res.begin(), res.end()); return res; } long long vectoll(std::vector<long long> vec, long long base = 10) { long long res = 0; std::reverse(vec.begin(), vec.end()); for (auto i : vec) { res *= base; res += i; } std::reverse(vec.begin(), vec.end()); return res; } std::vector<long long> lltobin(long long n) { std::vector<long long> res; for (int i = 0; i < 32; ++i) { res.push_back(n % 2); n /= 2; } std::reverse(res.begin(), res.end()); return res; } long long bintoll(std::vector<long long> vec) { long long res = 0; for (auto i : vec) { res *= 2; res += i; } return res; } // 拡張ユークリッドの互除法(a*x + b*y = gcd(a,b) となる x,y を返す) ll extgcd(ll a, ll b, ll &x, ll &y) { if(b == 0) { x = 1; y = 0; return a; } ll g = extgcd(b, a % b, y, x); y -= (a / b) * x; return g; } // M と互いに素な a の逆元(M が合成数の場合でも、a と M が互いに素ならば拡張ユークリッド法で求める) ll modinv(ll a, ll M) { ll x, y; ll g = extgcd(a, M, x, y); if(g != 1) return -1; // 本来は起こらない(ここでは den について逆元を求めるので必ず gcd(den, M)=1) x %= M; if(x < 0) x += M; return x; } long long MOD = 998244353; static const int MAXN = 2000000; // 必要に応じて大きく設定 // 階乗・階乗逆元の配列 static long long fact[MAXN+1], invFact[MAXN+1]; // 繰り返し二乗法 (a^b mod M) long long modpow(long long a, long long b, long long M) { long long ret = 1 % M; a %= M; while (b > 0) { if (b & 1) ret = (ret * a) % M; a = (a * a) % M; b >>= 1; } return ret; } // 前処理: 階乗と逆元のテーブルを作る void initFactorials() { // 階乗テーブル fact[0] = 1; for (int i = 1; i <= MAXN; i++) { fact[i] = fact[i-1] * i % MOD; } // 階乗逆元テーブル invFact[MAXN] = modpow(fact[MAXN], MOD - 2, MOD); // フェルマーの小定理で逆元を計算 for (int i = MAXN; i >= 1; i--) { invFact[i-1] = invFact[i] * i % MOD; } } // 組み合わせ数 C(n, r) long long comb(int n, int r) { if (r < 0 || r > n) return 0; return fact[n] * invFact[r] % MOD * invFact[n - r] % MOD; } class uftree { private: std::vector<int> union_find_tree; std::vector<int> rank; std::vector<int> nodes_count; public: uftree (int uftree_size) { union_find_tree.resize(uftree_size); rank.resize(uftree_size); nodes_count.resize(uftree_size); for (int i = 0; i < uftree_size; ++i) { union_find_tree[i] = i; rank[i] = 0; nodes_count[i] = 1; } } int find (int n) { if (union_find_tree[n] == n) return n; else { union_find_tree[n] = find(union_find_tree[n]); return union_find_tree[n]; } } void unite (int x, int y) { x = find(x); y = find(y); if (x == y) return; if (rank[x] > rank[y]) { union_find_tree[y] = union_find_tree[x]; nodes_count[x] += nodes_count[y]; } else { union_find_tree[x] = union_find_tree[y]; nodes_count[y] += nodes_count[x]; if (rank[x] == rank[y]) rank[y]++; } } bool connected (int x, int y) { x = find(x); y = find(y); return x == y; } int groupCount (int x) { x = find(x); return nodes_count[x]; } }; class sumSegmentTree { int n; std::vector<long long> tree, lazy; public: sumSegmentTree(int size) { n = size; tree.assign(4 * n, 0); lazy.assign(4 * n, 0); } void push(int node, int start, int end) { if (lazy[node] != 0) { // 遅延値を現在のノードに適用 tree[node] += (end - start + 1) * lazy[node]; // 子ノードに遅延値を伝播 if (start != end) { lazy[node * 2] += lazy[node]; lazy[node * 2 + 1] += lazy[node]; } // 現在のノードの遅延値をクリア lazy[node] = 0; } } void updateRange(int l, int r, long long value, int node = 1, int start = 0, int end = -1) { if (end == -1) end = n - 1; push(node, start, end); if (start > r || end < l) { // 完全に範囲外 return; } if (start >= l && end <= r) { // 完全に範囲内 lazy[node] += value; push(node, start, end); return; } // 部分的に範囲が重なる場合 int mid = (start + end) / 2; updateRange(l, r, value, node * 2, start, mid); updateRange(l, r, value, node * 2 + 1, mid + 1, end); tree[node] = tree[node * 2] + tree[node * 2 + 1]; } long long queryRange(int l, int r, int node = 1, int start = 0, int end = -1) { if (end == -1) end = n - 1; push(node, start, end); if (start > r || end < l) { // 完全に範囲外 return 0; } if (start >= l && end <= r) { // 完全に範囲内 return tree[node]; } // 部分的に範囲が重なる場合 int mid = (start + end) / 2; long long leftQuery = queryRange(l, r, node * 2, start, mid); long long rightQuery = queryRange(l, r, node * 2 + 1, mid + 1, end); return leftQuery + rightQuery; } long long lower_bound(int l, long long value, int node = 1, int start = 0, int end = -1, long long curval = 0) { if (end == -1) { end = n - 1; push(node, start, end); curval += queryRange(0, l - 1); if (tree[node] < value + curval) return n; } if (start == end) { if (tree[node] + curval < value) return start + 1; else return start; } int mid = (start + end) / 2; push(node * 2, start, mid); push(node * 2 + 1, mid + 1, end); if (tree[node * 2] + curval >= value) { return lower_bound(l, value, node * 2, start, mid, curval); } else { curval += tree[node * 2]; return lower_bound(l, value, node * 2 + 1, mid + 1, end, curval); } } }; void fft(std::vector<std::complex<double>>& a, bool invert) { const double PI = acos(-1.0); int n = a.size(); for (int i = 1, j = 0; i < n; ++i) { int bit = n >> 1; for (; j & bit; bit >>= 1) { j -= bit; } j += bit; if (i < j) std::swap(a[i], a[j]); } for (int len = 2; len <= n; len <<= 1) { double angle = 2 * PI / len * (invert ? -1 : 1); std::complex<double> wlen(cos(angle), sin(angle)); for (int i = 0; i < n; i += len) { std::complex<double> w(1); for (int j = 0; j < len / 2; ++j) { std::complex<double> u = a[i + j]; std::complex<double> v = a[i + j + len / 2] * w; a[i + j] = u + v; a[i + j + len / 2] = u - v; w *= wlen; } } } if (invert) { for (std::complex<double> &x : a) { x /= n; } } } // fft()必須 std::vector<long long> convolution(const std::vector<int>& a, const std::vector<int>& b) { std::vector<std::complex<double>> fa(a.begin(), a.end()), fb(b.begin(), b.end()); int n = 1; while (n < (int)a.size() + (int)b.size() - 1) n <<= 1; fa.resize(n); fb.resize(n); fft(fa, false); fft(fb, false); for (int i = 0; i < n; ++i) { fa[i] *= fb[i]; } fft(fa, true); std::vector<long long> res(n); for (int i = 0; i < n; ++i) { res[i] = llround(fa[i].real()); } return res; } // Trieの各ノードを表すクラス class TrieNode { public: unordered_map<char, TrieNode*> children; bool is_end; int count; TrieNode() : is_end(false) { count = 0; } }; // Trie木を管理するクラス class Trie { private: TrieNode* root; public: Trie() { root = new TrieNode(); } // 文字列をTrie木に挿入するメソッド void insert(const string &word) { TrieNode* node = root; (node->count)++; for (char c : word) { // 子ノードが存在しなければ新たに作成する if (node->children.find(c) == node->children.end()) { node->children[c] = new TrieNode(); } node = node->children[c]; (node->count)++; } // 最後のノードを単語の終端とする node->is_end = true; } // 単語がTrie木に存在するか完全一致検索するメソッド bool search(const string &word) { TrieNode* node = root; for (char c : word) { if (node->children.find(c) == node->children.end()) { return false; } node = node->children[c]; } return node->is_end; } // 指定された接頭辞を持つ単語が存在するか確認するメソッド bool startsWith(const string &prefix) { TrieNode* node = root; for (char c : prefix) { if (node->children.find(c) == node->children.end()) { return false; } node = node->children[c]; } return true; } int sameStartsLen(const string &word, const int sameCount) { TrieNode* node = root; int res = 0; for (char c : word) { if (node->children.find(c) == node->children.end() || (node->children[c])->count < sameCount) { return res; } node = node->children[c]; res++; } return res; } }; vector<string> rot(vector<string> s, int h, int w) { vector<string> res = s; for (int i = 0; i < h; ++i) { for (int j = 0; j < w; ++j) { res[i][j] = s[h - j - 1][i]; } } return res; } class PersistentUnionFind { struct Node { // (time, parent) のペアの履歴 vector<pair<int, int>> parent_history; // (time, size) のペアの履歴 vector<pair<int, int>> size_history; }; public: vector<Node> nodes; int current_time; // 初期化:各要素は自分自身が親で、サイズは1 PersistentUnionFind(int n) : nodes(n), current_time(0) { for (int i = 0; i < n; i++) { nodes[i].parent_history.push_back({0, i}); nodes[i].size_history.push_back({0, 1}); } } // 時刻 t における要素 a の親を二分探索で取得する int get_parent(int a, int t) { auto &ph = nodes[a].parent_history; int l = 0, r = ph.size(); // ph[i].first が t 以下となる最後のペアを探す while (l < r) { int mid = (l + r) / 2; if (ph[mid].first <= t) { l = mid; } else { r = mid; } } return ph[l - 1].second; } // 時刻 t における要素 a の集合サイズを二分探索で取得する int get_size(int a, int t) { auto &sh = nodes[a].size_history; int l = 0, r = sh.size(); while (l < r) { int mid = (l + r) / 2; if (sh[mid].first <= t) { l = mid + 1; } else { r = mid; } } return sh[l - 1].second; } // 時刻 t における要素 a の代表元を再帰的に求める int find(int a, int t) { int pa = get_parent(a, t); if (pa == a) return a; return find(pa, t); } // union 操作を行い、current_time を更新する // a と b の集合を併合する(併合はサイズの小さい方から大きい方へ) void union_sets(int a, int b) { current_time++; // union 操作ごとにタイムスタンプを進める // 直前の状態での代表元を取得する a = find(a, current_time - 1); b = find(b, current_time - 1); if (a == b) return; // 既に同じ集合なら何もせず終了 // サイズで併合する(小さい集合を大きい集合に統合) int size_a = get_size(a, current_time - 1); int size_b = get_size(b, current_time - 1); if (size_a < size_b) swap(a, b); // a を b の親にする更新を記録 nodes[b].parent_history.push_back({current_time, a}); // a の集合サイズを更新して記録 int new_size = size_a + size_b; nodes[a].size_history.push_back({current_time, new_size}); } // 時刻 t において a と b が同じ集合に属しているかを返す bool same(int a, int b, int t) { return find(a, t) == find(b, t); } }; std::vector<long long> getDivisors(long long N) { std::vector<long long> divisors; for (int i = 1; i <= std::sqrt(N); ++i) { if (N % i == 0) { // i が約数の場合 divisors.push_back(i); if (i != N / i) { // i が平方根でないとき、対となる約数も追加 divisors.push_back(N / i); } } } // 約数を昇順にソートする(任意) std::sort(divisors.begin(), divisors.end()); return divisors; } unsigned long long isqrt(unsigned long long n) { unsigned long long left = 0, right = n; unsigned long long ans = 0; while (left <= right) { unsigned long long mid = left + (right - left) / 2; // mid * mid <= n とするとオーバーフローの危険があるので、mid <= n / mid を使う if (mid <= n / mid) { ans = mid; // mid*midが n 以下であれば解候補に更新 left = mid + 1; // より大きい値を探索 } else { right = mid - 1; } } return ans; } void outdouble(long double a) { std::cout << std::fixed << std::setprecision(10) << a << std::endl; } int main() { ll a, b; cin >> a >> b; ll ans = gcd(a, b); a /= ans, b /= ans; ans *= gcd(a + b, ans); cout << ans << endl; }