Strorkisのブログ

信憑性については悪しからず

yukicoder No.990 N×Mマス計算(Kの倍数)

No.990 N×Mマス計算(Kの倍数) - yukicoder

 op + *のどちらかである時、 C_{i,j} = A_i\ op\ B_j Kの倍数であるような (i, j)の組合せは何通りあるかという問題です。

WAを抜け出せなかったので解説に沿って解いたのですが、解いていて楽しかったので記事を書くことにしました。

(全面的にC++における表現を使用しています)

解法

op == '+'の場合

 A_i+B_j Kの倍数となるのは、 A_i,\ B_j Kで割った余りの和が Kの倍数である時。

そのため、予め B_j\ \%\ Kの値ごとにカウントしておくことで、各 A_iにおいて (K-A_i\ \%\ K)\ \%\ Kの値をカウントしておいた中から探すだけで済むようになる。

注意
  •  K-A_i\ \%\ Kとすると、 A_i\ \%\ K==0の時、 B_j\ \%K==Kの値を探してしまう。
  • C++では) -A_i\ \%\ Kとすると、負の値が出力されてしまう。
  • カウンターは 1 \leq K \leq 10^9 1 \leq N,M \leq 10^5であることから、配列ではなくmapを使用する。
コード
    map<int, int> dict;
    for (int i = 0; i < m; i++) {
      int b; cin >> b;
      dict[b % k]++;
    }
    for (int i = 0; i < n; i++) {
      int a; cin >> a;
      ans += dict[(k - a % k) % k];
    }

op == '*'の場合

 A_i*B_j Kの倍数となるのは、 A_i,\ B_j Kの最大公約数の積が Kの倍数である時。

そのため、予め B_j Kの最大公約数の値ごとにカウントしておくことで、各 A_iにおいて全ての B_jとの最大公約数の積が Kの倍数である時、それぞれの個数の積を計算するだけで済む。

注意
  •  Kの約数の個数は、二重ループで間に合う範囲内である。
  • カウンターは 1 \leq K \leq 10^9であることから、配列ではなくmapを使用する。
  • 最大公約数の積と個数の積は整数オーバーフローする。
コード
    /* typedef pair<int, int> P; */

    map<int, int> dict1;
    map<int, int> dict2;
    for (int i = 0; i < m; i++) {
      int b; cin >> b;
      dict1[gcd(b, k)]++;
    }
    for (int i = 0; i < n; i++) {
      int a; cin >> a;
      dict2[gcd(a, k)]++;
    }
    for (P p1 : dict1) {
      for (P p2 : dict2) {
        if (1LL * p1.first * p2.first % k == 0) {
          ans += 1LL * p1.second * p2.second;
        }
      }
    }

一言

記事を書くのが面倒になっているせいか、説明が適当になってしまっているので、余裕ができるまでしばらく休止した方が良いのかもしれません。