面白かった!!!
「2 で何回割れるのか」に着目するのはあるあるだけど、400 点としては難しめな感じかな。
問題概要
個の正の整数
と整数
が与えられる。以下の条件を満たす整数
が何個あるか求めよ。
- 任意の
に対して、ある 0 以上の整数
が存在して、
が成立する
制約
は 2 以上の偶数
考えたこと
まず は偶数なので、全部 2 で割っておくと、実質的に問題は
のすべての系列に含まれるような整数 (
以下) の個数を求める問題ということになる。ここで、いきなり考えると大変なので、2 つの系列について考えてみることにする。
2 系列問題
の両方に含まれるということは
を満たす奇数 が存在するということ。ここで、
は奇数なので、
と
の「2 で割れる回数」は等しくなければならない
ということがわかる。なぜなら と
とをそれぞれ素因数分解したときに、双方の 2 の指数が等しくなければならない。が、
にも
にも 2 が含まれないので、結局
における 2 の指数と、
における 2 の指数が等しくなければならない。
逆に、 と
の 2 の指数が等しいならば、
(
は奇数)
(
は奇数)
としたときに、 は、
となる。つまり、元の問題において「
も
も奇数の場合」に帰着されたわけだ。こうなると実は簡単で、
と
の公倍数は、
と
の最小公倍数を
として、
の倍数である
- ただし、
とか
とかはダメ。
とかだと
が偶数になってしまう
が条件を満たす
まとめると、
を 2 で割れる回数が等しいことが必要なので、あらかじめ 2 で割れるだけ割ってしまう (
もついでにその回数分だけ
=
/ 2 (小数点切り捨て) とする)。そして、
として、
が答え
一般の場合
ここまで 2 個の場合を考えたけど、 個の場合も同様。
を「2 で割れる回数」が等しいことが必要なので、あらかじめ 2 で割れるだけ割っておく (
も)
として、
が答え
#include <iostream> #include <vector> using namespace std; long long GCD(long long x, long long y) { if ( y == 0) return x; else return GCD(y, x % y); } long long solve(int N, long long M, vector<long long> &a) { // 2 で割れるだけ割る (割れる回数が異なったらダメ) bool same = true; while (a[0] % 2 == 0) { for (int i = 0; i < N; ++i) { if (a[i] % 2 != 0) return 0; a[i] /= 2; } M /= 2; } for (int i = 0; i < N; ++i) if (a[i] % 2 == 0) return 0; // lcm long long lcm = 1; for (int i = 0; i < N; ++i) { long long g = GCD(lcm, a[i]); lcm = lcm / g * a[i]; if (lcm > M) return 0; } return (M / lcm + 1) / 2; } int main() { int N; long long M; cin >> N >> M; vector<long long> a(N); // あらかじめ a は一回 2 で割っておく for (int i = 0; i < N; ++i) cin >> a[i], a[i] /= 2; cout << solve(N, M, a) << endl; }