結果
| 問題 |
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;
}