面白かった!!!
「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; }