0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

競プロ参戦記 #72 Modularness | ABC 156

Last updated at Posted at 2020-02-26

AtCoder Beginner Contest 156 に参加したので、各問題の考察を書いていきます。結果はE完でした。

A - Beginner

問題概要: ButCoder の会員は内部レーティングと表示レーティングを持つ。その会員のコンテスト参加回数が10以上のときは、表示レーティングは内部レーティングに等しい。そうでなければ参加回数を K として、表示レーティングは内部レーティングから 100(10-K) を引いたものに等しい。コンテスト参加回数が N で、表示レーティングが R の会員の内部レーティングを求めよ。

A: 考察

求める内部レーティングを X とします。

N≥10 のとき、表示レーティングと内部レーティングは等しいので、X = R です。

N < 10 のとき、R = X - 100(10-K) なので X = R + 100(10-K) になります。

筆者の回答 (Rust, AC): https://atcoder.jp/contests/abc156/submissions/10273771

B - Digits

問題概要: 整数 N を K 進数で表したときの桁数を求めよ。

B: 考察

K=10 で考えます。

小数点の移動を考えると分かりやすいかもしれません。例えば 31415 は5桁ですが、小数点を5回左に移動して 0.31415 とすると小数点が左端に来ます。

  • 31415.0
  • 3141.5
  • 314.15
  • 31.415
  • 3.1415
  • 0.31415

つまり、5桁の整数をちょうど5回 10 で割ると、はじめて整数部が 0 になりました。これを一般化して、「h 桁の整数をちょうど h 回 K で割ったら整数部が 0 になる」ような h を計算すれば OK です。

    let mut x = N;
    let mut h = 0;
    while x > 0 {
        x /= K;
        h += 1;
    }
 
    println!("{}", h)

筆者の回答 (Rust, AC): https://atcoder.jp/contests/abc156/submissions/10272846


形式的に考える場合は、まず「n が K 進数表記で h 桁である」を言い換えなければいけません。これは、0 以上 K 未満の整数からなる長さ h の数列 $(x_i)$ で、次の2つの条件をともに満たすものがあることです:

  • $x_{h-1} \ne 0$ (一番上の桁がゼロでないこと)
  • $n = \sum_{i=0}^{h-1} x_i K^i$

次に n を変形します。(イメージは 31415 ≤ 99999 < 10^5)

\begin{align}
n  & = \sum_{i=0}^{h-1} x_i K^i & \\
   & ≤ (K-1) \sum_{i=0}^{h-1} K^i       & (x_i ≤ K-1 だから) & \\
   & = (K-1) \cdot \frac{K^h-1}{K-1}    & (\sum_{i=0}^{n-1} x^i = \frac{1-x^n}{1-x} だから) & \\
   & = K^h-1 \\
   & < K^h
\end{align}

したがって $\frac{n}{K^h} < 1$ なので、n を K で h 回以上割ればで整数部が 0 になるといえます。

一方、h-1 回目まで整数部が 0 にならないことは次の評価で示せます。(イメージは 31415/10^4 = 3.1415 ≥ 3 ≥ 1)

\begin{align}
\frac{n}{K^{h-1}}
    & = \frac{\sum_{i=0}^{h-2} x_i K^i}{K^{h-1}} + x_{h-1} \\
    & ≥ x_{h-1} \\
    & ≥ 1 \\
\end{align}

C - Rally

問題概要: N 個の座標 X(i) が与えられる。座標 P に対して、(X(i) - P)^2 の総和をコストとする。コストの最小値を求めよ。

  • 制約: 1≤X(i)≤100

C: 考察

座標 P と各位置 X(i) との距離 |X(i) - P| が大きいほどコストが重くなります。言い換えると、P は X(i) に近いほどコストが小さいです。そのため、P は真ん中=平均値にするのが最適です。

とはいえ、制約から 1≤P≤100 が分かるので、この100通りをすべて試せばうかつな WA のリスクもなくせて、安心です。

筆者の回答 (Rust, AC): https://atcoder.jp/contests/abc156/submissions/10272019

D - Bouquet

問題概要: n 種類の花が1本ずつある。1本以上の花からなる花束を作る。ただし、ちょうど a 本またはちょうど b 本からなる花束は作れないとする。作れる花束の種類の数を求めよ。(mod 10^9 + 7)

D: 考察

まず「1本以上」「ただし」を無視して、n 種類のものから好きな個数を選ぶ場合の数を考えます。部分集合の個数なので、2^n 通りです。ここから次の3パターンの花束を除外します。

  • 0本の花束
  • ちょうど a 本の花束
  • ちょうど b 本の花束

制約から 0 < a < b なので、これら3パターンは互いに排反であり、単純に場合の数を引けばいいです。

0本の花束は1通りです。n 種類の花からちょうど a 種類を重複なく選ぶ場合の数は C(n, a) で表せます。b の方も、同様に、C(n, b) です。

問題は n が巨大なので、C(n, a) の計算を (n!)/(a! (n-a)!) でやる階乗DPが間に合わないことです。高校数学で C を手計算するときのように、以下の式を使えば O(a) 時間で間に合います。

C(n, a) = (n/a)・((n-1)/(a-1))・((n-2)/(a-2))・...・((n-a+1)/1)

筆者の回答 (Rust, AC): https://atcoder.jp/contests/abc156/submissions/10278435

E - Roaming

問題概要: n 個の部屋がある。はじめ、各部屋にはちょうど1人いた。ちょうど k 回、人の移動 (1人が異なる部屋に移動すること) が起こった。建物の各部屋の人数の組み合わせの場合の数を求めよ。(mod 10^9 + 7)

E: 考察

「ちょうど」k 回、というのが厄介そうですが、n≥3 なので実は無視できます。k 回未満の移動で目的の組み合わせにできるなら、「無駄な移動」を挟むことでちょうど k 回の移動にできるからです。

例えば 部屋1 → 部屋2 という1回の移動を、部屋1 → 部屋3 → 部屋2 という2回の移動にできます。

そのため、無駄な移動を考える必要はありません。特に、誰かが退室した部屋に別の人が入る、というケースは考えなくてよいです。このことから、部屋を「はじめにいた人が退室する部屋」か否かで分類することが考えられます。

「はじめにいた人が退室する部屋」の個数を z (z≤k) とします。退室した人がどこに行くかは自由なので、これら z 人を残りの n-z 部屋に分配するすべての可能性があります。これは重複組み合わせで計算できます。

筆者の回答 (Rust, AC): https://atcoder.jp/contests/abc156/submissions/10284738

F - Modularness

問題概要: 長さ k の非負整数列 d(i) が与えられる。q 個のクエリのそれぞれにおいて、非負整数 n, x, m が与えられる。a(j) を以下のように定めるとき、a(j) % m < a(j+1) % m となる整数 j (0≤j < n-1) の個数を求めよ。

  • a(0) = x
  • a(j+1) = a(j) + d(j % k)

F: 考察

整数の切り捨ての除算を x // y で書くことにします。

a(j) % m < a(j+1) % m となる j を数える代わりに、この不等式が成り立たない j を数えて n-1 から引くことにします。

a(j) % m ≥ a(j+1) % m となる j の個数は、次の2種類の条件を満たす j を別個に数えて足すことで求まります。

  • a(j) % m = a(j+1) % m
  • a(j) % m > a(j+1) % m

a(j+1) は定義に従って展開できます。

  • a(j) % m = (a(j) + d(j % k)) % m
  • a(j) % m > (a(j) + d(j % k)) % m

(=) の方は d(j % k) = 0 となる j の数で数えられるので、以下 d(j % k) ≠ 0 とします。

(>) の方は、a(j) に d(j % k) を足して余りが減ったということは余りが「一巡した」ということなので、a//m = (a + (d % m))//m - 1 がいえます。

  • a(j) % m ≤ (a(j) + d(j % k)) % m のとき
    • a(j)//m = a(j+1)//m
  • a(j) % m > (a(j) + d(j % k)) % m のとき
    • a(j)//m = a(j+1)//m - 1

数列 (a(j)//m) の値が増える回数と > の不等号が成立する回数が一致するので、> の不等号を満たす j の個数は a(n-1)//m - a(0)//m で求まります。

解説AC: https://atcoder.jp/contests/abc156/submissions/10311747

余りを使う問題で商を使う発想がなかったのが反省点でした。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?