A. Phoenix and Gold
総量が $x$ であるとダメです.そうでない場合 $2$ 種の $a,b$ を候補として,「$a$ を追加して違反するならばやめて $b,a$ の順に追加」とすればよいです.全体の種類数が $2$ 以上ならば pairwise distinct がなくてもできます.
B. Phoenix and Puzzle
$2n^2$ や $4n^2$ と書ける場合 YES です.それ以外ダメな証明は,一辺の長さが $a+b\sqrt{2}$ と書けなければならないことからできます.
C. Phoenix and Towers
ひとつずつ追加していきます.その時点で高さ最小のところに置くようにすれば,常に max, min の差が $x$ 以下である状態が保たれます.
D. Phoenix and Socks
まずコスト $0$ でなるべくたくさんとってよいです.そのあとコスト $1$ でとれる対を最大化します.
左・右の個数が同数になったらこれらを全部コスト $1$ でマッチできます.大きい方のグループで色がそろっているものについて,コスト $1$ でマッチさせていきます.
E. Phoenix and Computers
自分で置くものを o で表すと,oooox/oooox/oox/oox/ooo のように分割できます.手前から順に作っていって,作った長さと自分で置いたものの個数の組を状態とします.
末尾に ooox を追加する部分が問題ですが,これはひとつ置いたあと,「左端につける」「右端につける」のどちらかを適当な回数繰り返すという操作手順が対応して,適当な $2$ のべき乗がかかります.これに,「これまでの列に対する操作」「この部分に対する操作」を振り分ける二項係数をかけて遷移します.
F. Phoenix and Earthquake
$x+b_i$ のものと $x+b_j$ のものがマージされると $x+(b_i+b_j)$ になり,$b_i=a_i-x$ を考える方が簡潔だと思います.
「マージすると和になる,ただし和が $-x$ 未満になる操作は禁止」というルールになります.
総和が $-x$ であることが必要で,必要条件のもと,値が最大の成分を隣接成分とマージする操作が常に可能であることが分かります.
G. Phoenix and Odometers
強連結成分ごとに独立に解けばよいです.
サイクルを任意にとれば,強連結性より $v$ から $v$ からへの $2$ つの walk でそのサイクル部分しか差がないものがとれるので,これは $t$ の倍数でなければいけません.逆に任意のサイクル長が $t$ の倍数であれば,適当な始点からのひとつ選んだ walk の重みを potential として,$\text{mod} t$ の potential を定めることができて,条件を満たします.よって強連結成分ごとに potentialized unionfind で解けます.
H. Phoenix and Bits
熱烈データ構造問題.
Binary Trie を作ります.まず次ができます.
- split:$[l,r)$ 部分とその他に分割
- merge: $2$ つの Binary Trie をマージ
マージは,片方が空なら自明,そうでなければ「左の子同士をマージしたものを新しい左の子とする」「右の子同士をマージしたものを新しい右の子とする」として再帰的に行えます.この計算量は,「非自明なので再帰」が 1 回行われるたびに全体のノード数が減少するので,(ある木のコピーを作ったりしていない限りは)ノードを作る回数と同程度の計算量と思ってよいです.
次に XOR に対応します.これはよくある遅延伝搬を使えばよいです.あるノードにそれを反映させるときに,その桁が反転されるならば左右の子を入れ替えます.またこれを実装した時点で,merge, split を含めてあらゆる木の探索の際に,潜る前に push することを徹底する必要があります.
最後に AND, OR に対処します.Segment Tree Beats! と同様の発想です.
そのクエリが XOR としても扱えるならば,そのようにします.そうでない,つまり「$2^i$ の位が $0, 1$ のノードがどちらも存在する状況で,それらをそろえるようなクエリ」については,左右の子ノードに対して再帰的に計算してから,その桁部分の反映をします(何もしない / 左右の子をマージなど).
この実装をするには,各ノードと桁に対して,左方向の辺・右方向の辺が存在するかという情報も持っておけばよいです.
計算量は,「余分な計算が行われる」たびに「何かの種類数が減る」という形になっているので Beats! のようなポテンシャル解析で抑えられるという話です.具体的にはすべてのノードと桁に対する「左方向の辺・右方向の辺が存在するか」という値の sum をポテンシャルとして解析できます.これは AND, OR の再帰が余分に発生するたびに $1$ 以上減少してくれます.増加はマージに関わるすべてのノードのすべての桁が戻ってしまう分です.
そのような回数は結局全体でのノード生成回数で抑えられるという話でしたから,何もかもが split で作られるノードの個数に桁の個数をかけたもので抑えられていて,$\log^2$ 計算量になります.
木構造の split, merge などをいくつか自分で書いたことがあれば実装の難所は少ないと思いますが,コンテスト中に実装するのは大変そうです.
I. Phoenix and Diamonds
残金が 「$[2^k,2^{k+1})$ のときに使うデータ構造」を各 $k$ について準備します(コスト $2^k$ 以下の商品しかないときは $[2^k,\infty)$ にまとめてもよい).
この $k$ を残金レベルということにします.
アイテムを heavy, light に分類します.light とは $2^k$ 未満であるもの.
- 残金レベルが下がらない間は,light で購入できない事態は発生しないというのがポイントです.
- heavy 購入に成功すると必ず残金レベルが下がる
というのがポイントです.結局
- 最初に購入可能な heavy のところまで購入する
- light だけを残金のレベルが下がるところまで購入する
の $2$ タイプのイベントが発生するタイミングを調べて,そのうち先に起こるイベントのところまでの計算をまとめて行います.
クエリごとにレベルが下がる回数と,セグメント木二分探索で,それぞれアイテム最大コスト,$N$ の $\log$ が計算量につきます.