目次
AtCoder Beginner Contest 465 D,E,F問題メモ
D - X to Y
問題文
- 整数 $X,Y$ と $2$ 以上の整数 $K$ が与えられます。
- 変数 $x$ があり、はじめ $x=X$ です。あなたは $x$ に対して以下の操作を $0$ 回以上何回でも行うことができます:
- $\displaystyle \left\lfloor \frac xK \right\rfloor=y$ または $\displaystyle \left\lfloor \frac yK \right\rfloor=x$ を満たす整数 $y$ を選び、$x$ の値を $y$ に置き換える。
- ここで、実数 $z$ に対し $\displaystyle \left\lfloor z \right\rfloor$ は $z$ 以下の最大の整数として定義されます。
- $x=Y$ とするために必要な操作回数の最小値を求めてください。
- $T$ 個のテストケースが与えられるので、それぞれについて答えを求めてください。
制約
- $1\le T\le 2\times 10^5$
- $0\le X,Y\le 10^{18}$
- $2\le K\le 10^{18}$
- 入力される値は全て整数
解法
$K$ 進数の数として考えると見通しが良くなる。
- $\displaystyle \left\lfloor \frac xK \right\rfloor=y$ は、$K$ 進数で表した $x$ の下1桁を削る行為
- $\displaystyle \left\lfloor \frac yK \right\rfloor=x$ は、$K$ 進数で表した $x$ の下1桁に好きな数を付け足す行為
よって、$K$ 進数で表した時の互いの prefix が共通する部分は残してよくて、それ以外は削ったり付け足したりする必要がある。
とはいえ、実装の際には prefix とか考えずとも、「$X=Y$ となるまで $X$ と $Y$ の大きい方を $K$ で割る」操作回数を求めることで正答できる。
E - Digit Circus
問題文
- $1$ 以上 $N$ 以下の整数 $x$ であって、以下の $3$ つの条件のうちちょうど $\mathbf{1}$ つだけを満たすものの個数を $998244353$ で割ったあまりを求めてください。
- $x$ は $3$ の倍数である
- $x$ の十進表記には
3が含まれる - $x$ の十進表記にはちょうど $3$ 種類の数字が使われる
- なお、整数の十進表記は先頭に不要な
0をつけないものとします。
制約
- $N$ は整数
- $1 \leq N \lt 10^{500}$
解法
桁DP。1つ1つの条件は桁DPの問題として度々見るが、複数を一気に考えなければならないので落ち着いて処理する。
なお、「1つだけ満たす」などの文言から包除原理っぽくも見えるが、どうせ包除原理でも複数条件を同時に満たすやつを一気に考える必要がある。 それなら後述のようなDPで「どの条件を満たしてて、どれを満たしてないか」を個別に判定することができるので、DPは1回でいい。
以下の情報を整理しとけばいい。
- $\mathrm{DP}[i,j,k]:=$
- 上から $i$ 桁目まで見て、$N$ 以下であることが確定していて、
- これまでに使った数字の集合bitsetが $j$ で、
- ここまでのmod3が $k$ であるような数字の個数
bitsetは、「十進表記に3が含まれるか」「十進表記は丁度3種類の数字が使われるか」の2つの判定に答えられる。
状態数は $2^{10}=1024$
mod3 は、そのまま $3$ の倍数かの判定に用い、状態数は $3$
遷移では、各状態から末尾に新規に何を置くかで $10$ 通り。
桁は $500$ 桁なので、全体で $1024 \times 3 \times 10 \times 500 = 1.536 \times 10^7$ 程度で、間に合う。
F - Sjeltzer?
問題文
- ある冷蔵庫では、ドリンクに $6$ 桁の数字列を ID として割り振っています。
- この冷蔵庫には $N$ 個のドリンクが保管されています。
- $i$ 番目のドリンクの ID は $S_i$、サイズは $V_i$ です。$S_1, \dots, S_N$ は相異なります。
- $Q$ 個のクエリに答えてください。各クエリは $6$ 桁の数字列 $x, y$ の形式で与えられ、内容は以下の通りです。
- この冷蔵庫にあるドリンクのうち、割り振られた ID $s$ がどの $k = 1, \dots, 6$ についても以下を満たすようなものについて、そのサイズの総和を求めよ。
- $s, x, y$ の $k$ 桁目の数字が表す値を $s_k, x_k, y_k$ とおいたとき、$x_k \leq s_k \leq y_k$ を満たす。
制約
- $N$ は整数
- $1 \leq N \leq 3 \times 10^5$
- $S_i$ は数字 (
0-9) からなる文字列 - $|S_i| = 6$
- $S_1, \dots, S_N$ は相異なる
- $V_i$ は整数
- $1 \leq V_i \leq 10^9$
- $Q$ は整数
- $1 \leq Q \leq 3 \times 10^5$
- 各クエリにおいて、$x, y$ は数字 (
0-9) からなる文字列 - 各クエリにおいて、$|x| = |y| = 6$
解法
仮にIDの桁が1桁なら、「IDが $x$ 以上 $y$ 以下のドリンクのサイズの総和は?」となって、単純な累積和問題であることはすぐ分かる。
これを6次元でやれ、という問題。
例えば2次元累積和なら以下の4つの点の足し引きで求められる。これは包除原理を使っている。
→2次元目
↓ (X1-1,X2-1). … (X1-1,Y2).
1 ┼───────┼ Acc[Y1,Y2] - Acc[Y1,X2-1] - Acc[X1-1,Y2] + Acc[X1-1,X2-1]
次 │ │
元 │ │
目 ( Y1 ,X2-1).│ (Y1,Y2).│
┼───────┼
6次元でも同様に、$2^6$ 個の累積和の位置を参照し、「$x_k-1$ と $y_k$ のうち、$y_k$ の方を採用した個数」が 偶数なら答えに正で寄与し、奇数なら負で寄与する、として合計すれば答えが求まる。 累積和を計算しておけば、1クエリ当たり $2^6=64$ 個の値の配列参照と加減算で求められる。
各IDを桁毎にバラして $(s_1,s_2,s_3,s_4,s_5,s_6)$ の位置に重み $v$ を加算してから、各次元の累積和を取る。
この際、実際に6次元配列でやってもいいのだが、配列の入れ子は(特にPythonでは)重く・遅くなる上に、for の入れ子をいっぱい書かないといけなくなる。
高速ゼータ変換・メビウス変換のような方法で、1次元配列で管理することもできる。
zeta = [0] * 1000000
// 各IDの位置にドリンクのサイズを加算後、
for d in (次元数,今回は6):
for _id in (000000から999999まで):
if _id の d 桁目が"0"でないなら:
zeta[_id] += zeta[_id の d 桁目を1減らした数]
としていくと、累積和が取れる。
※ただし、累積和では $[L,R]$ の範囲を求める際、$Acc[R]-Acc[L-1]$ を計算する時に $L=0$ の場合でも統一的に実装するため、先頭に $0$ を追加しておく実装もあるが、今回のこのやり方ではそれができなくなる。
- 各次元のサイズを $11$ に広げ、IDの各桁の数字を1ずつ加えて解釈する。
- 足し込む座標、if の部分等に少し工夫が必要となる。
- $L=0$ の場合は引く処理をスキップする
などの回避策が必要となる。

