1
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.

競プロ参戦記 #74 Divisible Substring | ABC 158

Posted at

AtCoder Beginner Contest 158 のA-E問題の考察を書いていきます。

本番は寝過ごしたので終了直後にやってみたところ、ぎりぎりE完 (3WA) という感じでした。

A - Station and Bus

問題概要: 市内には3つの駅がある。i 番目の駅は鉄道会社 S(i) が管理している。鉄道会社 A が管理している駅と、鉄道会社 B が管理している駅の間には、バスが運行することになった。これらの駅の間にバスが運行しているかどうかを判定せよ。

制約:

  • |S| = 3
  • S(i) は A または B

A: 考察

S に A と B の両方が含まれていたら、それらの駅の間にバスが運行するので Yes、そうでなければ No となります。

「A と B の両方が含まれる」ような S は6通り、逆に「A と B の片方しか含まれない」のは2通りです。後者を検査した方が短く書けます。

    S != "AAA" && S != "BBB"

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

B - Count Balls

問題概要: 列の末尾に A 個の青いボールを追加し、列の末尾に B 個の赤いボールを追加する、という操作を 10^100 回行う。列の前から N 個のボールのうち、青いボールの個数を求めよ。

制約: A+B, N≤10^18

B: 考察

整数の端数切り捨ての除算を x//y と書きます。

例えば A=3, B=2 なら、ボールの列は以下のようになります。10^100 が目を引きますが、単に列の長さが N 以上であるといっているだけです。

青青青赤赤 青青青赤赤 青青青赤赤 青青青赤赤...

「青青青赤赤」のような A+B 個のボールからなるブロックは、先頭 N 個のボールに N//(A+B) 個含まれています。そのうち青いボールは、1ブロックあたりA個なので、(N//(A+B))・A 個です。

N が (A+B) の倍数でなければ、最後の1ブロックの途中までが「先頭 N 個」に含まれます。最後のブロックには N % (A+B) 個のボールが含まれています。そのうち青いボールは、前から A 個だけなので min(N % (A+B), A) 個です。

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

C - Tax Increase

問題概要: ある商品は、消費税率が 8% のとき A 円の消費税が課され、消費税率が 10% のとき B 円の消費税が課される。商品の税抜き価格がこの条件を満たすことがありうるか判定し、ありうるなら最小の税抜き価格を求めよ。(消費税の端数は切り捨てられる。)

制約: A, B≤100

C: 考察

x 以下の最大の整数を floor(x)、x 以上の最小の整数を ceil(x) と表します。

条件を満たす税抜き価格を X とおくと、次の連立方程式が立ちます。

  • floor(X・8/100) = A
  • floor(X・10/100) = B

floor を使わない形に変形します。floor(x) は x を超えないので floor(x) ≤ x であり、最大なので x-1 < floor(x) です。

  • X・8/100 - 1 < A ≤ X・8/100
  • X・10/100 - 1 < B ≤ X・10/100

X について変形すると:

  • X < (A+1)・100/8
  • A・100/8 ≤ X
  • X < (B+1)・100/10
  • B・100/10 ≤ X

まとめると:

  • max(A・100/8, B・100/10) ≤ X < min((A+1)・100/8, (B+1)・100/10)

したがって、X の最小値 u と最大値 v が求まります。

  • u = max(ceil(A・100/8), B・100/10)
  • v = min(ceil((A+1)・100/8), (B+1)・100/10) - 1

u > v なら X は存在せず、u≤v なら X=u が最小です。

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

C: 別解

制約から X < 10^4 なので、全探索で求まります。このごろ全探索が見えてなくて反省……

D - String Formation

問題概要: 英小文字からなる文字列 S が与えられる。次の Q 個の操作を順番に行った後の文字列を求めよ。

  • i 番目の操作は以下の2種類のどちらか:
    • T(i)=1 のとき: 文字列 S を反転する。
    • T(i)=2 のとき: 整数 F(i) と英小文字 C(i) が与えられる。
      • F(i)=1 のとき: 文字列 S の先頭に C(i) を追加する。
      • F(i)=2 のとき: 文字列 S の末尾に C(i) を追加する。

制約: |S|, Q≤10^5

D: 考察

実際にこれらの操作を行うと TLE します。

反転操作を実際には行わず、文字列が反転状態にあるかどうかをフラグで持てば、反転操作を最後まで遅延できます。また、先頭への追加と末尾への追加は、列を両端キュー (VecDeque) で持つことにより高速化可能です。

全体として O(|S| + Q) 時間で間に合います。

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


「操作列の変形」という視点で考えると面白いと思います。

先頭に文字 c を追加する操作を「先頭(c)」、末尾への追加操作を「末尾(c)」と書くことにします。

例えば S=cde, 操作=(先頭(b), 末尾(f), 反転, 先頭(g), 末尾(a), 反転) という入力をやってみると、以下の通りです。

操作 操作後
cde
先頭(b) bcde
末尾(f) bcdef
反転 fedcb
先頭(g) gfedcb
末尾(a) gfedcba
反転 abcdefg

「反転→先頭(c)」という操作列は「末尾(c)→反転」に、「反転→末尾(c)」という操作列は「先頭(c)→反転」という操作列に、それぞれ変形しても最終結果が変わりません。この変形を可能なかぎり適用すると、反転操作を操作列の最後に移動できます。

操作 操作後
cde
先頭(b) bcde
末尾(f) bcdef
末尾(g) bcdefg
先頭(a) abcdefg
反転 gfedcba
反転 abcdefg

2回連続の反転は無視していいので、反転は最大1回になります。

また、先頭への追加操作と末尾への追加操作も交換できます。つまり「先頭(a)→末尾(b)」と「末尾(b)→先頭(a)」は、最終的には同じ結果になります。そのため、先頭(c)を前方に、末尾(c)を後方にまとめても OK です。

操作 操作後
cde
先頭(b) bcde
先頭(a) abcde
末尾(f) abcdef
末尾(g) abcdefg

したがって、操作列は「先頭に文字列 X を追加する」「末尾に文字列 Y を追加する」「n 回反転する」というかたちに変形できます。クエリを見て X, Y, n を求め、(X + S + Y) を計算して、n 回反転したら答えが得られます。

実際に書くコードは上記の回答とだいたい同じです。

E - Divisible Substring

問題概要: 数字からなる長さ N の文字列 S と、素数 P が与えられる。S の空でない区間で、それを10進表記の整数をみなした際に素数 P で割り切れるものの個数を数えよ。(先頭が 0 で始まる10進表記の整数も認める。)

制約: N≤10^5

E: 考察

例えば S=31415, P=3 だと、次の4つの区間があります。

    31415
    ^       3
    ^^^^    3141
     ^^^    141
       ^^   15

S の l 文字目から r-1 文字目までの文字列を10進表記の整数とみなしたものを f(l, r) とおきます。f を使って問題を形式的に書きなおすと:

f(l, r) ≡ 0 (mod P) となるペア (l, r) の個数を求めよ (0 ≤ l < r ≤ N)

となります。

f(l, r) をすべて計算するのは O(N^2) 時間かかって間に合いませんが、r=N に固定した f(l, N) だけなら、累積和により O(N) 時間で計算できます。一般の f(l, r) は f(l, N) の式で表せそうなので、そのあたりから考察が進みそうです。

f(l, r) を f(r, N) の桁数だけずらしたものに f(r, N) を足すと f(l, N) に等しくなる (例えば 31415 = 31 * 1000 + 415 のようになる) ことから、次が成り立ちます。

$$
f(l, N) = 10^{N-r} f(l, r) + f(r, N)
$$

もし P が 10 と互いに素なら 10^(N-r) で両辺を割れるので、f(l, r) について変形して:

$$
f(l, r) = 10^{r-N} (f(l, N) - f(r, N))
$$

を得ます。こうして次が分かりました。

\begin{align}
    f(l, r) & ≡ 0 \\
    ⇔ f(l, N) & ≡ f(r, N) \quad (\text{mod}\ P)
\end{align}

言い換えると、f(i, N) % P の値が等しいような i を2つもってきてそれぞれ l, r とすれば f(l, r) ≡ 0 (mod P) が成り立つということです。

したがって、f(i, N) % P の値で i を分類し、各剰余に対応する i の個数が n 個であれば n(n-1)/2 個のペアを計上すれば OK です。全体として O(N) 時間で解けました。


忘れてはいけないのが P が10と互いに素でないケース、つまり P = 2 と P = 5 です。このとき 10 ≡ 0 (mod P) だから 10 は逆元を持たないので、上記の「両辺を 10^(N-r) で割る」操作は NG です。

P = 2 のケースは簡単で、偶数の10進表記は一番下の桁が偶数なので、

f(l, r) ≡ 0 (mod 2)
⇔ S(r-1) が偶数

といえます。したがって、r に関してループを回すことで O(N) 時間で解けます。

P=5 も同様に、5の倍数の10進表記は一番下の桁が 0 か 5 であることを利用して解けます。

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

1
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
1
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?