ABC108のCで詰まった話
問題
ABC108のCの解説がモロ載っているのでまだ解いていない方は注意してください。
問題文
整数 N,K が与えられます。N 以下の正の整数の組 (a,b,c) であって、a+b,b+c,c+a がすべて K の倍数であるようなものの個数を求めてください。 ただし、a,b,c の順番を入れ替えただけの組も異なるものとして数えます。また、a,b,c の中に同じものがあっても構いません。
制約
1≤N,K≤2×105
N,K
は整数である
解説を読んでも理解するまで時間がかかったので、どうやって解けばいいのかを書いていきます。 分からない方への解説となれればうれしいです。
そもそもの解説文
C: Triangular Relationship
Kが奇数の時、a; b; cをKで割ったあまりはすべて0である必要があります。Kが偶数の時、a; b; cをKで割ったあまりはすべて0であるか、あるいはすべてK/2である必要があります。 このような組の個数は、N以下でKで割って0あまるものの個数とK=2あまるものの個数から求めることができるので、この問題を解くことができました。
なんで???????????????????
わからん。「多分整数問題で式変形して簡単に求める方法があるんだろうな」って思って色々変形してわからんかったから解説見たんだけど何もわからん。 整数問題に弱すぎる。
以下解法です。
modを使った解法
がすべて の倍数になるってどういう時かを考えると、
and and または、
and and
しかありません。
(他の分数形のあまりだとのどれかが の倍数にならない)
よって、Kが奇数だとあまりが0のときのみ(K/2が整数にならないので)、偶数だとあまりが0とK/2のときとなります。
これこの記事書いている途中に気付きました あ~~~~~~、なるほどね、そりゃそうだって感じだ。ずっと、次に示す解法で解こうとしていたので時間がかかりました…。
式変形から求める解法
整数問題にありがちな(本当にそうか?)方程式の片側が整数だからもう片側も整数っていうやつを利用します。
とします。
この時
だから、
より、
も整数、よって
も整数となります。
ここで、の偶奇について考えます。
が奇数の時
を整数にするためには、は偶数なのでがの倍数になる必要があります。
ここで、
でしたから、となります。 これがの倍数になるためにはがの倍数になればいいです。 よって、はの倍数です。
についても同じように言えるので、はの倍数となります。
つまり、
a; b; cをKで割ったあまりはすべて0
です。
が偶数の時
奇数の時と同様に
a; b; cをKで割ったあまりはすべて0
に加えて、
あるいはすべてK/2
が許されます。
これは、偶数より、と置くと、
となるからです。(奇数の時は分子のと割れませんでした。)
よってはの倍数となればよく、より、だから、はの倍数です。
つまり、と表せます。ここでが偶数ならで割った余りは0、奇数なら割った余りがになります。
同様に、も求められます。
また、
を考えれば
Kが偶数の時、a; b; cをKで割ったあまりはすべて0であるか、あるいはすべてK/2である必要があります。
です。
これで、Kの偶奇により条件が違うことがわかり、それぞれで条件を満たす個数を数えればよくました。
組み合わせの個数
組み合わせは何を選ぶかなので、してやればいいです。
偶数の時の数え方に注意しましょう。
提出したコード
以下のようなコードを書けばいいです。
#include<bits/stdc++.h> using namespace std; int main(void){ int n, k; cin >> n >> k; int cnt = 0, ans; for(int i = 1; i <= n; i++){ if(i % k == 0) cnt++; } ans = pow(cnt, 3); if(k % 2 == 0){// k偶数 cnt = 0; for(int i = 1; i <= n; i++){ if(i % k == (k / 2)) cnt++; } ans += pow(cnt, 3); } cout << ans << endl; }
まとめ
競プロの整数問題はmodで考えたらわかりやすいことが多い気がします。
というか普通の式変形はしんどい……
modで考えよう!!!!!!!!!!!!!!!!!!!!!!!!!!!
あと、pow使うとたまに手元と、提出先のものとで計算結果が違うことがあるんですが同じような人いますかね…(丸め誤差?)