physics0523's 精進ログ

主に競プロの面白い問題の解法をメモします

整数値を取り $f(k) \le n/k$ のように制約され単調減少する $f$ に対して、普通にひとつ計算すると $O(g(n))$ かかるところを $f(1),f(2),\dots,f(n)$ をまとめて $O(g(n) \sqrt{n} \log{n})$ で計算するテク

Problem - D - Codeforces (CFR507-D1D) に使いました

 

以下の条件を満たす $f$ を取り扱います。

  • $f$ は整数値を取る
  • $f(k) \le N/k$ のように制約される ( $n$ 個のものをどうにかして $k$ 個ずつ個包装したい場合など… )
  • $f$ は(広義)単調減少する

$f$ を普通にひとつ計算すると $O(g(n))$ かかるとしましょう。この時、以下の手順で $f(1),f(2),\dots,f(n)$ をまとめて $O(g(n) \sqrt{n} \log{n})$ で計算できます。

 

まず、 $f(1)$ と $f(n)$ を普通に計算する。

次に、以下の分割統治を定義する。

  • [ $l,f(l),r,f(r)$ ] の値を渡しながら再帰的に解く。ここでは $f(l)$ から $f(r)$ までを管理する。
  • $l+1 \ge r$ ならば続行する意味が無いので終了。
  • $f(l)=f(r)$ ならば $f(l)=f(l+1)= \dots =f(r)$ が判明して終了。
  • $m = \lfloor \frac{l+r}{2} \rfloor$ とし、 $f(m)$ を求める。
  • 最後に、以下のふたつを呼ぶ。
    • [ $l,f(l),m,f(m)$ ]
    • [ $m,f(m),r,f(r)$ ]

これで [ $1,f(1),n,f(n)$ ] を呼べば目的達成。

 

ざっくりした計算量の証明:

$f$ は整数値を取り、その値の種類数は $O(\sqrt{n})$ である。この全部について $f$ がある値以上か未満かの二分探索をすることを考えるが、これでアクセスする部分には上の分割統治でもアクセスされ、アクセスされない部分にはアクセスされないはずである。なので $O(\log n)$ が掛かって全体で $O(g(n))$ を $O(\sqrt{n} \log{n})$ 回呼ぶことになる。

 

最初に挙げた問題での実装例: Submission #227612376 - Codeforces

ばちゃ中に再発明したけど違う方も同じことやってるコード書いてたし実はけっこう有名そう。もっといい名前とか付いてるのかな

Rusty String (CFR423-D1E)

Problem - E - Codeforces

問題概要

V, K, ? からなる文字列 $S$ が与えられる。 ?ワイルドカードである。

以下の条件を満たす $1$ 以上 $|S|$ 以下の整数 $k$ を全て求めよ。

  •  $S$ 中の ?V, K のどちらかに適切に置き換えたところ、 $S$ は周期 $k$ を持つ文字列であった。
    •  $k+1$ 以上 $|S|$ 以下の全ての整数 $i$ について $S$ の $i-k$ 文字目と $i$ 文字目とが一致するとき、文字列 $S$ は周期 $k$ を持つと言う。

例えば $S=$ V??VK であるとき、 $S$ を VKKVK と置き換えるとこの文字列は周期 $3,5$ を持つ。他の周期は作れないので求めるべき整数の集合は $\{3,5\}$ である。

例えば $S=$ ?VK? であるとき、 $S$ を KVKV と置き換えるとこの文字列は周期 $2,4$ を持つ。また、 $S$ を KVKK と置き換えるとこの文字列は周期 $3,4$ を持つ。他の周期は作れないので求めるべき整数の集合は $\{2,3,4\}$ である。

 

制約

・$1 \le |S| \le 5 \times 10^5$

 

知らないと解けない系。これ自力で思いつくのは無理だと思います。

やることは ワイルドカードを含むパターンマッチング - Qiita に書いてある。

モチベーションとしては以下の感じ。

$S$ と reversed $S$ とで上手いこと畳み込みをしたい。

$A_k = \displaystyle \sum_{i+j=k} S_i S_{|S|+1-j}$ みたいな式を作ると $A_{|S|+1-d}$ が $i-j=d$ なる $S_i S_j$ の総和となる。こんな感じの行為をすると「場所の差が $d$ である文字の比較」が全ての $d$ に対して $O(|S| \log |S|)$ で出来そう。但し今回はこの式そのままでは上手く行かない。

モチベーションとしては、以下の条件を満たす関数を設計して $A_k = \sum_{i+j=k} f(S_i,S_{|S|+1-j})$ を取りたい。

  • $f(x,y) = 0$ ( $x$ と $y$ とがマッチする場合)
  • $f(x,y) > 0$ ( $x$ と $y$ とがマッチしない場合)

これが実現すれば、 $A_d > 0$ であることと 距離 $d$ のマッチしない文字組が存在することとが同値になる。

実は ? $=0$,  V $=1$,  K $=2$ と対応させると  $f(x,y) = xy(x-y)^2$ としてやると所望の関数が出来上がる。天才か???

 $f(x,y) = xy(x-y)^2 = x^3y - 2x^2y^2 + xy^3$ とバラすと、 $x,x^2,x^3,y,y,y^2,y^3$ を書き並べた列を畳み込むことで $A_k = \sum_{i+j=k}$ が取れる。 

これで「距離 $d$ にマッチしない文字組が存在するか?」の配列が得られた。 $S$ が周期 $k$ を取らないことと、ある $k$ の整数倍の整数 $l$ について距離 $l$ にマッチしない文字組が存在することとは同値なので、 $O(|S| \log |S|)$ の調和級数ゲーをやれば判定が出来る。

Submission #210741528 - Codeforces

ワイルドカードを含むパターンマッチングを FFT 使って高速に捌くやつ、初履修…

Paths (CFR440-D)

Problem - D - Codeforces

問題概要

整数 $N$ が与えられる。

$N$ 頂点 $0$ 辺のグラフ $G$ を用意する。頂点番号は $1,2,\dots,N$ である。

$\gcd(i,j) \neq 1$ を満たす全ての整数組 $1 \le i < j \le N$ に対し、 $G$ の頂点 $i,j$ 間に長さ $1$ の無向辺を張る。

$1 \le i,j \le N$ なる整数組に対し、以下の関数 $f$ を定義する。

  • $f(i,j) =$ ( $G$ 上での頂点 $i,j$ 間の最短距離) ( 頂点 $i,j$ が $G$ 上で連結 )
  •      $0$ $( \rm{otherwise} )$

$\displaystyle \sum^{N}_{i=1} \sum^{N}_{j=i+1} f(i,j)$ を求めよ。

制約

・ $1 \le N \le 10^7$

 

まず簡単に実験をしてみる。

$N=14$ の場合 (ハイフンは到達不能)

色々実験してみると、

  • $2p > N$ なる素数 $p$ からは他の誰にも到達できなさそうなこと
  • $f(i,j) \le 3$ が成り立ちそうなこと

がわかるので、証明しにかかる。

まず $x$ が 素数であり $2x > N$ である場合、 $x$ は孤立頂点となり全ての $i$ に対し $f(x,i) = 0$ である。

また $1$ も孤立頂点であるので全ての $i$ に対し $f(1,i)=0$ である。

相異なる $i,j$ に対して $f(i,j) = 1$ であることの必要十分条件は $\gcd(i,j) \neq 1$ 。辺の貼り方より明らか。

ここまでで処理されていない $f(i,j)$ を吟味していく。ここで、 $\gcd(i,j) = 1$ であるケースを考えていくことに注意されたい。

$f(i,j)=2$ ということは $i,j$ の間に $1$ 人仲立ちを立てれば到達可能になるということ。ではどのような仲立ちを立てるべきか?

答えは ( $i$ の最小素因数 ) $\times$ ( $j$ の最小素因数 ) 。これが考えうる最も小さい仲立ちである。この値が $N$ 以下ならこの仲立ちが存在していることになる。

では、到達可能なら $f(i,j) \le 3$ が示せるだろうか?答えは Yes 。証明は以下。

「到達可能」という前提から $i,j$ の最小素因数の $2$ 倍は $N$ 以下であることに注意されたい。ここで、

 $i$ から ( $i$ の最小素因数 $\times$ $2$ ) に

 ( $i$ の最小素因数 $\times$ $2$ ) から ( $j$ の最小素因数 $\times$ $2$ ) に

 ( $j$ の最小素因数 $\times$ $2$ ) から $j$ に

直接移動できることが分かる。これより題意は示された。

 

さて、問題は以下に帰着される。

・$f(i,j) = 0$ if $i$ が孤立頂点 or $i=j$

・$f(i,j) = 1$ if $\gcd(i,j) \neq 1$

・$f(i,j) = 2$ if ( $i$ の最小素因数 ) $\times$ ( $j$ の最小素因数 ) $\le N$

・$f(i,j) = 3$ otherwise

が分かったので、元の問題を解け。

但し複数に該当する場合より上の条件を優先する。

 

$\gcd(i,j) = 1$ かつ ( $i$ の最小素因数 ) $\times$ ( $j$ の最小素因数 ) $\le N$ なる $i,j$ の数を数えに行くのが素直だが、かなり厳しそうなので方針転換。

「最初は全ての孤立頂点でない相異なる頂点の組 $i,j$ に対して $f(i,j)=3$ だと思って、うまいこと値を引いていけないか?」を考える。

・ $\gcd(i,j) \neq 1$ なら 1 減算 ... (i)

・( $i$ の最小素因数 ) $\times$ ( $j$ の最小素因数 ) $\le N$ なら 1 減算 ... (ii)

ができれば上手くいきそう。

しかし、  (i) かつ (ii) でない、即ち $\gcd(i,j) \neq 1$ だが ( $i$ の最小素因数 ) $\times$ ( $j$ の最小素因数 ) $> N$ であるような組 $i,j$ があればそこの値が $2$ として取り扱われてしまい、困ってしまう。

しかし、驚くべきことにそのような $i,j$ の組は存在しないことが証明できる。証明は以下。

$\gcd(i,j) \neq 1$ である相異なる $i,j$ の組を考え、それが (ii) を必ず満たすことを示す。

$\gcd(i,j) \neq 1$ なので $i,j$ に何らかの素因数が共通していることがわかる。

$i,j$ に $\sqrt{N}$ 以下の素因数が共通している場合、 ( $i$ の最小素因数 ) $\times$ ( $j$ の最小素因数 ) $\le N$ なのは共通している素因数より明らか。

逆に $i,j$ に $\sqrt{N}$ 超過の素因数しか共通していない場合、それを $p$ とおく (ここで、 $p$ は単一の素数であることに注意 )。

$i < j$ として一般性を失わない。

$i = p$ である場合、 $j$ が $\frac{N}{p}$ 以下の何らかの素因数を含んでいることになり、それと $p$ の積が $N$ となり題意を満たす。

また、 $i = p \times q$ ( $q$ は $2$ 以上の整数 ) である場合は ( $i$ の最小素因数 ) $\le q$ かつ ( $j$ の最小素因数) $\le p$ であるから $q \times p \le N$ より題意を満たす。

以上より示された。

あとはこれを実際に計算していく。

$\gcd(i,j) \neq 1$ かつ $i < j$ なる $i,j$ の組の数は オイラーのφ関数 を使えばエラトステネスの篩をやりながら求まり、 ( $i$ の最小素因数 ) $\times$ ( $j$ の最小素因数 ) $\le N$ なる組の数は最小素因数ごとにバケットソートすれば $O(N \log N)$ で求まる。

Submission #207821818 - Codeforces

$f(i,j)$ の求め方が分かってから、一見不味そうな数え上げを「 (i) かつ (ii) でないものは存在しない」という強力な補題を使って正当性を示しトドメを刺すのが美しい。

Something with XOR Queries (CFR440-B)

Problem - B - Codeforces

問題概要

長さ $N$ の $(0,1,\dots,N-1)$ の順列 $P$ (0-indexed) が存在し、隠されている。

$P$ の逆置換を $B$ とする。

以下の質問を高々 $2N$ 回使い、 $P$ を当てよ。

  • $0 \le i,j < N$ なる整数 $i,j$ を指定し、 $P_i \oplus B_j$ を尋ねる。

但し、異なる $P$ 同士でどう質問しても区別がつかないケースがあるので、当てた順列と区別がつかない $P$ の個数も数え上げよ。

 

制約

・ $1 \le N \le 5000$

 

誤った初手1: $N$ が奇数の場合は $P_i,B_0$ の総 xor を取って $(0,1,\dots,N-1)$ の総 xor と xor すれば $B_0$ が分かって $P$ が復元できる

偶数の場合進みません。悲しい。

誤った初手2: $0$ の位置を当てに行く

実験の結果 $0$ の位置が異なるのに区別がつかないケースがあって上手く行かなそう。悲しい2。

誤った初手3: 順列の構造を考える

偶数長の環が特定できないのかな~と思ったらそういうわけでもなさそうだし binary trie の左右入れ替えみたいな操作が区別つかないのかなと思ったらそういうわけでもなさそうだし… 悲しい3。

 

正しい初手: $2N$ 個の質問でありうる $N^2$ 個の質問全てに答えられるようにする

 

$P_i \oplus B_j$ の任意の値を答えられるようにしたい。

とりあえず脳死で $P_0 \oplus B_j$ と $P_i \oplus B_0$ を全部訊くことにする。

直接答えを訊いてない部分では $(P_x \oplus B_y) \oplus (B_y \oplus P_z) \oplus (P_z \oplus B_w) = P_x \oplus B_w$ の形で、最低 $3$ 手かかりそう。

$P_0$ と $B_0$ をターミナルみたいなもんだと思うと以下の式が浮かんでくる。

 $(P_i \oplus B_0) \oplus (B_0 \oplus P_0) \oplus (P_0 \oplus B_j) = P_i \oplus B_j$

これで、さっき脳死でした質問を使ってありうる質問全てに答えられることがわかった。

こうなればこっちのもん。制約 $N \le 5000$ より $P_0$ を $N$ 通り決め打って $P,B$ を復元して、

・$P$ が順列か否か

・$B$ が $P$ の逆順列になっているか

をチェックすればok。

 

Submission #207816383 - Codeforces

「ありうる質問全てに対する解答を復元しにかかる」という発想がキモかった。頭から抜け落ちてた。ただ「区別がつかない」ってそういうことなので「区別がつかない」が「ありうる質問全てに対する解答を復元しにかかる」という発想の枕詞なのかもしれない。

GCD Groups 2(CFR576-D1F)

Problem - F - Codeforces

問題概要

長さ $N$ の数列 $A$ が与えられる。

以下の条件を満たす $1,2$ からなる長さ $N$ の数列 $B$ があるか判定し、あるならひとつ構築せよ。

  • $B_i=1$ なる $i$ を並べた数列を $X$ とする。このとき、 $\gcd(A_{X_1},A_{X_2},\dots)=1$ である。
  • $B_i=2$ なる $i$ を並べた数列を $Y$ とする。このとき、 $\gcd(A_{Y_1},A_{Y_2},\dots)=1$ である。

$2 \le N \le 10^5$

$1 \le A_i \le 10^9$

 

要は $A$ をふたつに分けてそれぞれの $\gcd$ を $1$ にせよというもの。

$N \le 18$ 程度なら bit 全探索可能なのでそうする。

さらに、 $A_i \le 10^9$ より $A_i$ は相異なる素因数を高々 $9$ 種類しか含まないことが分かる。

漠然としすぎているので $1$ 側に含まれる要素を $1$ つ固定したとする。

ここでひとつ分かることがある。

$\gcd$ を取る過程で相異なる素因数の種類数をどんどん減らしていくことになるが、この時「素因数 $\alpha$ を消すために追加する要素 $A_i$ 」「素因数 $\beta$ を消すために追加する要素 $A_j$ 」... という感じで要素を追加しない限り、要素の追加は無駄であることが分かる。すなわち何が言いたいかというと、「ある解について、 $1$ 側に含まれる要素の個数を高々 $9$ 個とできる」ということが分かる。

ここで、大胆にも $2$ 側に含まれる要素も ランダムで $1$ つ固定したとする。

このとき、先ほどの「ある解」に含まれている要素を間違えて $2$ 側に含んでしまう確率は高々 $\frac{9}{N-1}$ であることが分かる。

では、 $\frac{N-10}{N-1}$ 以上の確率で生き残っている「ある解」を掘り起こすことを考えよう。

まず、 $dp[$ ( $1$ 側の素因数のうち残っているものの集合) ( $2$ 側の素因数のうち残っているものの集合) $]$ のようなことをすれば $O(2^{18}N)$ で解けるだろう。

このままでは非常に重いですが、「ある素因数 $p$ が含まれない」ような要素は $p$ それぞれについて高々 $18$ 個しか調べる必要がないことが分かります。何故なら、 $19$ 個目を追加するタイミングではもう $p$ を折る意味はなくなっているからです。(無駄にならない要素の追加は高々 $18$ 回なので、同じ素因数について $19$ 個目を入れておく意味がない)

これにより $O(2^{18}18^2)$ 程度の計算量で残りの問題が決定的に解けました。

これを $T$ 回回せば通る確率は $1 - \left ( \frac{1}{2} \right ) ^{T}$ 以上となり、時間いっぱい回せば $T \ge 10$ くらいにはなるのでこの問題に十分高い確率で正解できます。( $N$ が大きいほど bitDP の計算量がやや膨らみますが成功確率が上がるので、ちゃんと計算すればもうちょっと良い見積もりを得ます)

Submission #204169550 - Codeforces

決め打ち $1$ 回ごとの乱択の成功確率の見積もりが非常に美しい。

 

 

相異なる素数を小さい順に掛けた時の数表

項目 最大素因数 指数表記 備考
$1$ $2$ $2$ $2 \times 10^0$  
$2$ $3$ $6$ $6 \times 10^0$  
$3$ $5$ $30$ $3 \times 10^1$  
$4$ $7$ $210$ $2.1 \times 10^2$  
$5$ $11$ $2310$ $2.3 \times 10^3$  
$6$ $13$ $30030$ $3.0 \times 10^4$  
$7$ $17$ $510510$ $5.1 \times 10^5$ $10^6$ 制約の限界
$8$ $19$ $9699690$ $9.7 \times 10^6$  
$9$ $23$ $223092870$ $2.2 \times 10^8$ int の限界
$10$ $29$ $6469693230$ $6.5 \times 10^9$  
$11$ $31$ $200560490130$ $2.0 \times 10^{11}$ $10^{12}$ 制約の限界
$12$ $37$ $7420738134810$ $7.4 \times 10^{12}$  
$13$ $41$ $304250263527210$ $3.0 \times 10^{14}$ $10^{15}$ 制約の限界 
$14$ $43$ $13082761331670030$ $1.3 \times 10^{16}$  
$15$ $47$ $614889782588491410$ $6.1 \times 10^{17}$ long long の限界
$16$ $53$ $32589158477190044730$ $3.3 \times 10^{19}$  

A002110 - OEIS

間違ってたらごめんなさい

Unique Subsequence(ARC125-D)

D - Unique Subsequence

問題概要

長さ $N$ の数列 $A$ が与えられるので、(連続でなくともよい)部分列として取り出される方法が一意であるようなものを $\mod 998244353$ で数え上げよ。

$1 \le N \le 2 \times 10^5$

$1 \le A_i \le N$

最初 $dp[$ 要素 $x$ $]$ で $x$ がくるたびに預金を下ろすみたいな DP を考えたがそちらではうまくいかず。 $dp[$ 要素を取り出す位置 $i$ $]$ に方針を取り換える。

 

予め $arrow[i]=\{$ $A_i = A_j$ かつ $i<j$ なる最小の $j$ 、ただしそのような $j$ が存在しない場合は $N$ $\}$ を求めておく。

 

$i$ の昇順に $A_i$ を使うかどうかの DP を考える。

$dp[i]$ は、 $A_i$ を見る時点で 必ず「 $A_i$ までを見て、末尾として $A_i$ が採用された時の場合の数」が入っているようにする。

最初、 $dp[i]=1$ と初期化する。遷移は以下の通り。

  • $dp[arrow[i]]=0$ とする。これは、 $A_i$ を使っていないのに $A_i$ を飛び越えて $A_{arrow[i]}$ を使うことを禁止するため。
    • これにより、 $A_i$ と $A_j$ がこの順に隣接する区間で $A_j$ に起因する不都合がなくなる。
  • $dp[i+1,arrow[i]]$ の範囲に $dp[i]$ を加算する。この範囲内の $j$ であれば $A_i$ の直後に $A_j$ を置くことが許される。
    • これにより、 $A_i$ と $A_j$ がこの順に隣接する区間で $A_i$ に起因する不都合が生じない範囲での遷移が可能となる。
  • $arrow[i]=N$ であれば $A_i$ で締めてよいので、答えに $dp[i]$ を加算する。

Submission #40576826 - AtCoder Regular Contest 125

 

むちゃくちゃ頭壊しながらガチャガチャしたらなんかACしてしまったので、整理するために記事化。でもまだ全然頭壊れてる。