ARC091-D:Remainder Reminder(400)
問題
https://beta.atcoder.jp/contests/arc091/tasks/arc091_b
1 <= a, b <= n である a, b が与えられる。a % b >= k である個数を求めよ。
制約
- 1 <= n <= 105
- 1 <= k <= n-1
入力値は整数
考え方
a_n = {1, 2, 3, ..., n}, b_n = {1, 2, 3, ..., n} の2つの数列が与えられていると考えて良い。2つの要素が与えられて、ナイーブ解がO(N2)になるときの計算量を落とす時の典型は「片方を固定してもう片方については同じ要素/性質をまとめて数え上げる」である。ABC-Dでは非常によく出てくるテクニックである。
さて、今 b_i を固定するとしよう。どのように状態をまとめられるか考えてみる。
k = 0 の場合は明らかに n2 個存在する。以下では k >= 1 とする。
参考に N = 5 の場合について、a_i を b で割った余りを以下に記載する。
b | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|
1 | 0 | 0 | 0 | 0 | 0 |
2 | 1 | 0 | 1 | 0 | 1 |
3 | 1 | 2 | 0 | 1 | 2 |
4 | 1 | 2 | 3 | 0 | 1 |
5 | 1 | 2 | 3 | 4 | 0 |
(1)上記の表のように a_i を b で割ったときの余りは 1,.., b-1, 0 が繰り返される。繰り返される 1,.., b-1, 0 のまとまりは全部で floor(N/b) 回あらわれる。その中に k 以上の数は (b-k) 個ある。ただし b-k < 0 となる場合は 0 個とする。
(2)また、 1,.., b-1, 0 が floor(N/b) 回繰り返されたあとに残っている数は N % b 個ある。この中に k 以上の数が N % b - k + 1 個ある。ただし N % b - k + 1 < 0 となる場合は 0 個とする。
よって、ある b について求める個数は floor(N/b) * (b-k) + N%b-k+1 となる。これは O(1) で求められるので全体の計算量は O(N) となり間に合う。
public void solve(int testNumber, InputReader in, PrintWriter out) { long n = in.nextLong(), k = in.nextLong(); long sum = 0; for (int b = 1; b <= n; b++) { sum += max(0, b-k)*(n/b) + max(0, n%b-k+1); } if (k == 0) sum = n*n; out.println(sum); }
ポイント
- 2変数の場合は、片側固定で片方の状態をまとめて数え上げる。
- あまりは循環する