Amami’s competitive diary

Codeforces Round #653 D - Zero Remainder Array

問題概要

 n 要素の配列  A と整数  k が与えられる。
とある変数  x (初期値0)を用いて1回のうちに以下の操作のどちらかを行う。

  •  x の値を1増やす

  •  x の値を  A のうちのいずれかの要素  A_i に足して、 x の値を  1 増やす

 A のすべての要素を  k の倍数にするためには何回の操作が必要か。 codeforces.com

解説

全てを  k の倍数にするためには全ての  A_i について  (A_i 以上の  k の倍数  - A_i) を足せばいいので、各  A_i について  (k - A_i) mod  k を足せばいいことになります。

 x k 増やす間に  (k - A_i) mod  k 0 ~  (k - 1) をそれぞれ  1 つずつ使うことが出来ます。

ただしこの  (k - A_i) mod  k の値が被ることがあるので、その場合はまた  k 増やす必要があります。

一番被りが多い値のものについて考えれば、その間にそれ以外の値は  k の倍数にすることが出来るので、この一番被りが多い値のものについてのみ考えればいいです。

被りの回数  k 増やすので、(被りの回数) ×k を求めればいいのですが、最後の  1 回は必ずしも  k 増やすのではなく、この被りの一番多い値自体まで増やせばいいので、(被りの回数 - 1) × k+(被りの一番多いものの値)となります。

そして、 x は最初は  0 なので初めに  1 増やすことをふまえて先程の値に  +1すればいいことになります。

実装

#include<bits/stdc++.h>
using namespace std;

int main()
{
    int t;
    cin >> t;
    while(t--){
        long long n, k;
        cin >> n >> k;
        map<int, int> m;
        for(int i = 0; i < n; i++){
            int a;
            cin >> a;
            //kの倍数になるまでの足すべき値がそれぞれ何回出てくるかを数える
            //引き算なのでmodのとり方に注意
            if(a % k) m[((k - a) % k + k) % k]++;
        }
        //最頻値
        long long large = 0;
        //最頻値が何なのか
        long long tmp = 0;
        for(auto&& e : m){
            //それが最頻であったとき
            if(large <= e.second){
                large = e.second;
                //その値をメモする
                tmp = e.first;
            }
        }
        //最初から全てがkの倍数だった時は0
        if((int)m.size() == 0) cout << 0 << endl;
        //k * (最頻値 - 1) + 最頻値の足すべき値 + 1
        else cout << k * (large - 1) + tmp + 1 << endl;
    }
    return 0;
}

以上でこの問題を解くことが出来ました