ABC lessons learned10 - zettsu-t/zettsu-t.github.io GitHub Wiki
コンテストに参加した教訓をまとめていきます。
バチャではなく、一問ずつ解いた。考えがまとまらない。
コードはこちら
Success 、そうでなければ Failure を出力する。
コードはこちら
状態
-
loginなら$in$ を$True$ にする。前の状態は関係ない。 -
logoutなら$in$ を$False$ にする。前の状態は関係ない。 -
publicなら何もしない。 -
privateなら、$in = False$ の時に答えを1増やす。$in = True$ なら答えは変わらない。いずれにせよ状態を変えない。
コードはこちら
累積和だとすぐ分かったが添え字で悩んだ。
-
$C_{-1} = 0$ である -
$0 \leq i < K$ のとき、$C_i = i + 1$ である -
$i \geq K$ のとき、$C_i = C_{i-1} + C_{i-1} - C_{i-K-1}$ である
答えは
atcoder::modint::set_mod(1000000000);
using ModInt = atcoder::modint;実は累積和ではなく、スライディングウィンドウで解ける。
コードはこちら
方針はすぐ立ったが考えがまとまらない。
o の前後に . を置けないので、 o の前後を . で埋める。残った ? は o を置きたければ置ける場所であり、それらの値が o または . に一意に決まるならそのように決め、決まらないなら ? のままにする。
上記の通り埋めた後の o が ? をすべて . に置き換えたものが答えである。以下少なくとも一個以上の ? を o にしなければならない状況を考える。
? をランレングス圧縮する。ラン o を最大 o を置くと ? についても、 o に置き換えるか . にするか選択の余地があるので、すべての ? は ? のままである。
o に置き換える。奇数長のランは o.o.o と、 o で始まり o で終わり o. が交互に続くパターンしかないのでそのように ? を置き換える。偶数長のランは o 始まりと . 始まりの両方になりうるので、 ? のままである。
上記の通り ? を置き換えたものが答えである。公式解説1の通りである。
コードはこちら
方針はすぐ立ったが考えがまとまらない。
問題を以下のように読み替える。
- 頂点
$1..K$ だけからなるグラフ$G_k$ が連結かどうか調べる。連結ではない、つまり頂点1を含む連結成分$S_k$ のサイズが$K$ 未満なら-1を出力する。 - 頂点
$1..K$ だけからなるグラフから、 頂点$K+1..N$ に直接辺が出ているなら、その頂点集合$R_k$ を求める。上記で-1を出力しなければ、$|R_k|$ を出力する。
頂点
-
$j \in U$ について、$j < k$ なら頂点1と 頂点$j$ を連結する。 -
$R_k = R_{k-1} \setminus k$ で初期化する。つまり$R_k$ から$k$ を除く。 -
$j \in U$ について、$j < k$ なら$R_k$ から$j$ を除く -
$j \in U$ について、$j > k$ なら$R_k$ に$j$ を加える - 頂点1の連結成分の大きさが
$k$ なら(既述の通り頂点$1..k$ が連結なら)$|R_k|$ を出力する。そうでなければ-1を出力する。
計算コストは
コードはこちら
F問題はいつまで経っても解けないのにG問題は解けた。
答えは
具体的には
最後に、二分探索の結果
バチャではなく、一問ずつ解いた。
コードはこちら
コードはこちら
これまでに案内した人数を
- クエリ1は
$X$ を$V$ の末尾に追加する - クエリ2は
$V[P]$ を出力し、$P$ を1増やす。
コードはこちら
初期状態で、
-
$a \in F[j]$ について、$C[a]$ から$j$ を除く - この操作の前に
$C[a]$ が空ではなく、この操作の後で$C[a]$ が空になったら、料理$a$ を新たに食べられるようになったので答えを1増やす
コードはこちら
線分ではなく直線だった。問題を読み違えて時間が掛かった。
すべての直線の組が交わるとき、答えは
直線の向きは、
公式解説通りである。
コードはこちら
所持金
コードはこちら
半分全列挙だとは分かったが、TLEに苦しんだ。おそらく想定解法ではないが、数時間かけてごり押した。E問題が解けなくてF問題を解けるのはなぜだろう。
座標を0-based indexingとする。対角線
ある対角線のマスについて、左上からの累積値が
とにかく
バチャではなく、一問ずつ解いた。解くのが遅い。
コードはこちら
添え字を2ずつ増やす。
コードはこちら
? を置き換える方法は必ず
コードはこちら
- クエリ1は
$PS[X]$ に$Y$ を追加する - クエリ2は
$GS[X]$ を$True$ にする - クエリ3について
$Y \in PS[X] \lor GS[X]$ ならYes、そうでなければNoである。
コードはこちら
色々見落としが多い。
-
$DP[0][0] = 0$ ,$DP[0][1] = T[1]$ $DP[i+1][0] = max(DP[i][0], DP[i+1][1])$ $DP[i+1][1] = DP[i][0] + T[i]$
で更新する。このとき
コードはこちら
ローリングハッシュでまとめながら集合を管理と分かったが手こずった。
文字列
-
$S_1$ に含まれる文字列$T$ の$H(T,|T|)$ の集合を$X$ とする。$S_1$ 丸ごとのハッシュである。 -
$S_2$ に含まれる$j \in 1..|S_2|$ 番目の文字列$T = S_{2,j}$ に注目する。値が$h = H(T,1..|T|)$ となるような文字列の添え字の集合を$Y[h]$ とする。$S_2$ が持つ接頭辞の候補である。 -
$S_2$ の$j$ 番目の文字列が、$S_1$ のいずれかの要素が接頭辞になるかどうかを$U[j]$ として持つ。接頭辞なら1、接頭辞でなければ0である。$U$ は0-based indexingとしてセグメント木として持つ。
クエリ
クエリ
クエリ
-
$U[L] = 1$ なら既に$S_1$ の接頭辞なので何もしない -
$U[L] = 0$ かつ$h = H(S, 1..|S|)$ として、いずれかの$h$ が$X$ に含まれるなら$U[L] = 1$ とする。既に$T$ は$S_1$ の接頭辞である。 -
$U[L] = 0$ かつ$h = H(S, 1..|S|)$ として、いずれの$h$ も$X$ に含まないなら$h$ それぞれについて$Y[h]$ に$L$ を 追加する。今は$T$ は$S_1$ の接頭辞ではないが、将来は接頭辞にされるかもしれない、という予約である。 - 上記のいずれの場合も、
$L$ に1足す
コードはこちら
1,11,111,1111 をそのまま文字列にしたものをあらかじめ用意しておく。
-
()内は調べない。これは入れ子の深さが0以外かどうかで分かる。(で1深く、)で1浅くする。 -
$op = \times$ で$+$ が出たら、$B$ に括弧が要る。$op = +$ は調べなくてよい。
バチャではなく、一問ずつ解いた。E問題がわからない。
コードはこちら
出現した文字を数え、出現するかもしれない文字 a-z それぞれについて出現しなかったものを出力する。
コードはこちら
色を変えてから回転することと、回転してから色を変えることは等価である(回転後の場所の色を変えればいいので)。よって
コードはこちら
グラフにサイクルがあるか、と問題を読み違えて時間を溶かした。いったい何をしているのだろう。
辺の数が Yes 、そうでなければ No である。次数を無視すると、結び目があるグラフをサイクルグラフを誤認する。
コードはこちら
STLの柔軟性に助けられた。
std::map は std::vector<int> をキーに持つことができる。これを利用してDPを作る。
同じ動物園を二度までは見る(三度見る必要はない)ので、動物園を見る順番を
動物園
-
$S$ は、値が$\infty$ でない$DP$ のすべての要素とする -
$S+T = min(2, S[j] + T[j])$ とする $DP[S+T] = min(DP[S+T], DP[S] + C_i)$
答えは、動物を二回見たというキーに対する値
実は動物園を
コードはこちら
何時間考えてもわからない。MSTを作る問題だと思ったら全く違った。公式解説をほぼそのまま実装して解説ACした。後ろから考える典型である。
MSTを作ればよいのだが、合流する頂点を任意だと思ったので計算量が多すぎた。頂点
バチャではなく、一問ずつ解いた。発想は早いが実装が遅い。
コードはこちら
題意通り実装する。
コードはこちら
値が非0の
コードはこちら
要素
コードはこちら
多地点BFSなのだが、実装方法を忘れてTLEした。
距離
すべての出口をキューに積んでBFSし、それぞれの床についてある出口までの最短距離を求める。そのあとそれぞれの床について、自分より出口までの距離が短くなる方角を記入すると答えになる。
コードはこちら
組み合わせを高速に前計算すればよいが、実装方法を忘れて時間が掛かった。
並び方を列挙するのに、最も左にあるブドウに注目する。最も左にあるブドウより左に、バナナが
この方法で題意のうち、以下の二制約を必ず満たす。これは
- リンゴはすべてブドウよりも左側に並べる
- オレンジはすべてブドウよりも左側に並べる
最も左にあるブドウより左について、リンゴを左に、バナナを右に寄せ、間にオレンジを挟む。そうすると残りの制約である、リンゴはすべてバナナよりも左側に並べる、を満たす。これは
上記を前計算する。
${{D - 1} \choose {0}} = 1$ ${{D - 1 + i + 1} \choose {i + 1}} = {{D - 1 + i} \choose {i}} \times (D - 1 + i + 1) / (i + 1)$
${{A + B} \choose 0} = 1$ ${{A + B + i + 1} \choose {i + 1}} = {{A + B + i} \choose {i}} \times (A + B + i + 1) / (i + 1)$
この方法は公式解説1と同じである。
コードはこちら
実装方法が全く分からなかった。
公式解説が何種類かあるが、線分
バチャではなく、一問ずつ解いた。相変わらず、他の方の難易度と私の難易度が逆転する。
コードはこちら
Yes 、そうでなければ No である。
コードはこちら
任意精度整数を使って積を求め、積が
コードはこちら
方針はすぐ立ったが実装が進まなかった。
セグメント木
- チルダ型の1番目の点を
$i$ とする - チルダ型の2番目の点を
$L$ とする。これは$U[i..L-1]$ がすべて1となる最大の$L$ である。セグメント木のmax_rightを使って求める。 - チルダ型の3番目の点を
$M$ とする。これは$D[L..M-1]$ がすべて1となる最大の$M$ である。セグメント木のmax_rightを使って求める。 - チルダ型の4番目の点を
$R$ とする。これは$U[M..R]$ がすべて1となる最大の$R$ である。$[M,R)$ は題意を満たすので、$R - M$ を答えに加える。
公式解説はランレングスで解いている。
コードはこちら
C問題と異なり、あっさり実装が終わった。
問題とは
行
- クエリ1は、
$R[y]$ の要素数を答える。その後$x \in R[y]$ について、$C[x]$ から$y$ を除く。 - クエリ2は、
$C[x]$ の要素数を答える。その後$y \in C[x]$ について、$R[y]$ から$x$ を除く。
要素を除く回数は高々
コードはこちら
全く分からなかった。公式解説6を読んでC++で実装したのが上記のコードである。
E問題は解けないのにF問題は解ける。
コードはこちら
オイラーツアーを使って部分木の包含関係を構築する。
オイラーツアーのパスつまり
オイラーツアーにおける頂点の出入口を求める。頂点
オイラーツアーのパス長を要素数とするセグメント木
あとはクエリを組み立てる。
-
$T[In[i=1..N]] = 1$ で初期化する。すべての頂点の重みの和を、$S = N$ で初期化する。 - クエリ1は、
$T[In[x]]$ に$w$ を加算する。$S$ に$w$ を加える。 - クエリ2は、辺
$y$ の頂点$u,v$ のうち深い(頂点1からの距離が長い:$D[u], D[v]$ の大きい)方を$z$ とする。深さが同じならどちらでもよい。部分木の重み$W$ と、残りの部分木の重み$S - W$ の差が答えなので、$|S - 2 \times T[In[z],max(In[z],Out[z]-1)]|$ が答えである。
公式解説と同じ解き方である。
バチャではなく、一問ずつ解いた。
コードはこちら
コードはこちら
二つのサイコロの出目を総当たりし、該当する場合を
コードはこちら
ボタンAを押す回数は
答えは
コードはこちら
std::bitset<32>::set() が引数無しでコンパイルでき、しかもたまたま入力例が通ってしまったので1ペナした。
XORなので総当たりしかない。ドミノを既に置いた位置を std::bitset<32> で管理し、ドミノが置かれていないマスに書かれた整数すべてのビットごとの排他的論理和を
ドミノを置きたいマス
-
$[y,x]$ にドミノがなければ置いてもよい- ドミノを横に置く。つまり
$[y,x],[y,x+1]$ にはみ出さない範囲 ($x+1 \leq W$ ) で置く。$C$ を$C \oplus A_{y,x} \oplus A_{y,x+1}$ で更新する。 - ドミノを縦に置く。つまり
$[y,x+1],[y,x]$ にはみ出さない範囲 ($y+1 \leq H$ ) で置く。$C$ を$C \oplus A_{y,x} \oplus A_{y+1,x}$ で更新する。
- ドミノを横に置く。つまり
- 置かないで次の位置を調べる
コードはこちら
公式解説を読むまで全く手掛かりがつかめなかった。
コードはこちら
相変わらず、E問題は解けないがF問題は解ける。
ある
前準備として、セグメント木と遅延セグメント木を用意する。
-
$i = 1..N$ のうち、$A_i$ を既にみていたら1、まだ見ていないなら0に対応するセグメント木$T$ を作る。一点更新、区間minを取ることで、ある位置$i$ の前後にある、まだ見ていない位置が分かる。これはstd::setでもよい。 - 答えを遅延セグメント木
$U$ に載せる。区間長$j$ の答えを$U[j]$ とし、演算はすべて整数加算にする。公式解説によれば、普通のセグメント木といもす法でもよい。
-
$i \in S_v$ について、既に$S_v$ のうち見た位置を$M$ とする(なければ-1) -
$i$ の前後にある、まだ見ていない区間を$[L,R]$ とする。$T$ を二分探索すると求まる。少なくとも一点区間$[i,i]$ を含み、それより広いかもしれない。 - 他の
$S_v$ で既にみた区間を無視するために、$L = max(M+1, L)$ と更新する - 一点区間
$[i,i]$ に対応する答えとして$U[1]$ に$a$ を加算する -
$i$ を右端とする区間に対応する答えとして、$i$ を除く区間長$WL = i-L$ について$U[2..(1+WL)]$ に$a$ を一括加算する -
$i$ を左端とする区間に対応する答えとして、$i$ を除く区間長$WR = R-i$ について$U[2..(1+WR)]$ に$a$ を一括加算する -
$i$ を左端でも右端でもなく含む区間に対応する答えとして$WL$ と$WR$ のすべての和$w$ の組み合わせについて$U[w]$ に$a$ を加算する -
$M = R + 1$ と更新する
上記を一通り行い、
バチャではなく、一問ずつ解いた。翌朝ではなく4日間掛けて解いた。D問題で躓き、E問題の考察に時間が掛かり、F問題は実装が進まなかった。
コードはこちら
No 、そうでなければ Yes である。
コードはこちら
std::set に入れて一つずつ出力する。
コードはこちら
コードはこちら
コンテストの翌晩解いたら頭が回らなくて解けなかった。翌々朝に解いた。朝頭が冴えてないと考えがまとまらない。
連続する1の最左位置を
公式解説によると、累積minを求めると
コードはこちら
制約から察した。
3秒制限なのだから、
答え
辺の重みを
頂点
頂点
上記のように辺の重みを変えるか辺を除いたグラフについて、
コードはこちら
遅延セグメント木を使うことは一目瞭然だが、そこからが大変である。
足場が高い順にみる。この配列を
これは遅延セグメント木
コードを見た方が早いが、ループを以下の取り構成する。
- 足場が高い順に
$V$ から足場$H_j$ を取り出す-
$U$ から$H_j + D \leq H_i$ なる$H_i$ をそれぞれ取り出す -
$T[i] = \infty$ なら$T[i] = 1$ にする。そうでなければ$T[i]$ を1増やす。
-
- 答え
$A$ を$max(A, T[j-R, j+R])$ に更新する
上記の答え
バチャではなく、一問ずつ解いた。
コードはこちら
Yes 、なければ No である。
コードはこちら
コードはこちら
原点を入れ忘れて1ペナした。
円周の位置
正三角形とは位置
コードはこちら
問題文を理解できず泥沼になった。
連続部分文字列は長さ1以上である。ということは
そうではなく、
コードはこちら
D問題は全然わからないが、E問題はあっさりわかった。相変わらず、他人にとっての難易度と自分にとっての難易度が逆転する。
直感的にはこの通りである。葉つまり次数が1の頂点はどうにかして対消滅させなければならない。よって他の頂点を葉にするより、葉を他の頂点に移動する方がコストは同じか安いと考えられる。
よってキューに次数1の頂点を積んで、キューから取り出した頂点を隣に移動することを繰り返す。トポロジカルソートの要領である。
- 初期値は、元々次数1の頂点をすべてキューに積む。木なので、次数1の頂点は少なくとも一つある。
- キューから頂点を取り出し、隣の頂点に電荷を移動し、そのコストを足す。次数が1なので、隣の頂点は一意に決まる。
- 隣の頂点の次数を1減らし、次数が1になったらキューに積む。
これを繰り返すといつか頂点が無くなるので、コストを答える。
コードはこちら
マージテックっぽいがそうではない。AC or TLEで、TLEが取れないが、方針はあっているのに実装が悪かった。
連結成分そのものは、最大 std::priority_queue が正しく std::set だとTLEする。頂点
クエリには以下の通り答える。
- クエリ1は、
$C$ を1増やし、頂点$C$ について、初期値と同様に他の頂点までの距離を求めて$S$ を更新する - クエリ2は、
$S$ を昇順に見ていく。-
$(d,i,j)$ について、$(i,j)$ が連結成分なら除いて無視する -
$(d,i,j)$ について、$(i,j)$ が連結成分でなければ、最小の$d$ について頂点$(i,j)$ を$T$ でマージする。最小の$d$ 以外は見ない。
-
- クエリ3は、
$T$ において、$(i,j)$ が連結成分かどうか返す
この方針でTLEが取れないので公式解説を読んだら、上記の方法で正しかった。 std::priority_queue だと591 msec、 std::set だと2秒制限に間に合わずTLEする。こんなに実行時間が違うのか。C++だからといって定数倍を気にしないとこうなる。
ABC 376以来、ものすごく久しぶりにABCにratedで出場した。パフォ1509の快勝で水コーダーに復帰である。
コードはこちら
コードはこちら
-
$X \geq 1$ なら、箱$C[X]$ を1加算する -
$X = 0$ なら、箱$(C_i,i)$ を昇順にソートし、先頭要素の箱$y$ について$C[y]$ を1加算する
コードはこちら
典型的な、カーソルを使った読み替えである。
入力の
- クエリ1は、
$A[p+C] = x$ にする。$x$ は0-based indexingとし、入力から1引く。 - クエリ2は、
$A[p+C]$ を出力する。$x$ は0-based indexingとし、入力から1引く。 - クエリ3は、
$C = (C + k) mod N$ に更新する
コードはこちら
頂点
DFSですべての辺を探索する。
-
$DFS(0,0)$ を起点に探索する -
$W$ が$S[i]$ に含まれるなら、ループなので探索を打ち切る - そうでなければ
$W$ を$S[i]$ に加える。$i$ から有向きな隣の頂点$j$ について、辺$(i,j)$ の重みを$w$ として$DFS(j,W \oplus w)$ で探索する。
XORなのでループを二周すると0になる。よっていつかこの探索は終了し、
想定解法は頂点倍化である。
コードはこちら
いかにもナップザックDPである。倒せるモンスターの数を二分探索する。
モンスター
-
$DP[i][j=0..H]$ はモンスター$i$ を倒した後、体力$j$ のときの最低魔力を意味する - 初期値は
$DP[0][] = \infty, DP[0][0] = 0$ である -
$DP[i+1][j=0..H]$ を考える。体力を使い魔力を使わなければ、$DP[i+1][j+A_i] = min(DP[i+1][j+A_i], DP[i][j+A_i])$ である。ただし体力$H$ を超える更新はできない。 - 同様にを魔力使い体力を使わなければ、
$DP[i+1][j] = min(DP[i+1][j], DP[i][j] + B_i)$ である。ただし魔力$M$ を超える更新はできない。
この方針であっているが二分探索は要らなかった。
TLE解しか思いつかなかった。 #=1, .=-1 の二次元累積和をそのまま計算すると
コードはこちら
問題文通り実装する。
コードはこちら
累積和を求めながら出力する。
コードはこちら
集合演算で解こうとして異常に長いコードを書いてWAし、一時間経ってそうではないことに気が付いた。
センチネルを両端に加えて、長さ
要素
要素
コードはこちら
レジスタリネーミングの気持ちである。Tomasulo's algorithm で、 reservation stations がたくさん(
サーバとPCの番号をまとめて機器と呼び、サーバを
クエリを読むごとに世代を1増やす。つまりクエリに対して
- クエリ1に対して、
$A[p] = a$ と更新する。$G$ に辺$(a,A[0])$ を加えるよう$G$ に積む。 - クエリ2に対して、クエリ更新前の世代
$b = A[p]$ とし、$A[p] = a$ と更新する。$G$ に辺$(a,b)$ を加えるよう$G$ に積む。 - クエリ3に対して、
$A[0] = a$ と更新する。$G$ に辺$(a,A[p])$ を加えるよう$G$ に積む。
キューを末尾から先頭に向かって再生し、依存グラフを更新する。
- 初期値は
$a = A[0]$ とする - 辺
$(from, to)$ について、$a \ne from$ なら無視する - 辺
$(from, to)$ について、$a = from$ なら、答え$T$ に$S[to]$ の左右反転を加える。その後$a = to$ に更新する
コードはこちら
なぜE問題の方がC問題より簡単なのだろう。
最大値となりうる数は面の数字の種類数なので、最大
- (((各サイコロの上を向いた面に書かれた数)の最大値)の最小値)は、((サイコロ面の最小値)についてすべてのサイコロの最大値)である。形式的には
$L = max_i (min(A_{i,j=1..6}))$ である。 - サイコロ
$i$ には、値が$a$ となる面が$c > 0$ 個含まれるとする。これを$(i,c)$ と表現する。面の値の集合$F$ があり、$F[a]$ は$(i,c)$ の集合とする。$|F[a]|$ を$F[a]$ の要素数とする。
-
$S[i,a]$ を、サイコロ$i$ について、値が$a$ 以下となる面の数とする -
$b$ を、$M$ にある数で$a$ 未満の最大値とする。ただし$a$ が$M$ の最小値であれば$b=0$ とする。 -
$C[a]$ を、各サイコロのすべての面が$a$ 以下となる面の組み合わせ数とする。この値を$U$ として、$a$ を処理するたびに更新する。便宜上$C[0] = 0$ とする。 -
$a < L$ なら$C[a] = 0$ である。$F[a]$ に含まれる$(i,c)$ について、$S[i,a]$ に$c$ を足す。この加算は以下でも行う。 -
$a = L$ なら、$U = C[a] = \prod S[i,a] > 0$ である。 -
$a > L$ なら、$U = C[a] = \prod S[i,a]$ である。これを愚直に計算するとTLEするので、下記の通り差分更新する。
(((各サイコロの上を向いた面に書かれた数)の最大値)の最小値)が
コードはこちら
コードはこちら
Yes 、そうでなければ No である。入力例3のように、 Yes である。
コードはこちら
両端を固定するのがややこしい。
いかにもDPである。区間最小セグメント木
-
$S_{2..(N-1)}$ を昇順にソートする。$S_1$ と$S_N$ を固定することに注意する。 - 最初に
$T[i=2..N]$ について、$2S_1 \geq S_i$ なら$T[i] = 2$ 、$2S_1 < S_i$ なら$T[i] = \infty$ とする。 -
$i=3..N$ について、自分を倒せるドミノの位置を決める。これは$h = \lceil S_i / 2 \rceil$ として、$h \leq S_{j=2..(i-1)}$ を二分探索する。 -
$T[i] = min(T[i], 1 + min(T[h,i-1]))$ と更新する。
-1 と出力する。
想定解法は貪欲法らしい。
コードはこちら
実装が重いのだが、たぶんもっと良い方法がある。
操作後の
頂点を組に分割したら、連結成分
-
$E$ に含まれて、$G$ 含まれない無向辺を追加する。この操作回数を$A$ とする。 -
$G$ の辺のうち、$S$ に含まれる頂点からなるグラフ(誘導部分グラフ)を$T$ とする。$T$ に含まれて、$E$ 含まれない無向辺を削除する。この操作回数を$B$ とする。 -
$S$ について$A+B$ の最小値を求める
この操作を行うと、ある組について、連結成分についての操作回数
こうしてある組について
公式解説1は、連結成分が2個以下であることを利用している。他の解説では
コードはこちら
D問題が分からずE問題を先に解いた。エッジケースの作り込みに苦しんだ。
少なくとも答えは
最後まで印が付かない数は素数である。よって素数の個数を答えに加える。これらの和が答えである。とにかく等号不等号の扱いがややこしい。
コードはこちら
荷物を詰めるたびに、カバンの容量を減らす。
コードはこちら
問題文通り総当たりする。
コードはこちら
ランレングスをキューに積んで順に取り出す。
コードはこちら
エッジケースを取りこぼすと29 WAsするマルチテストケースが容赦ない。
自明な例として、 Yes である。
Yes 、そうでなければ No である。
それ以外は比が Yes 、そうでなければ No である。
コードはこちら
敢えてE問題から解いてみたが、何を思ったか制約を読み違えて全探索したらTLEした。正しくは以下の通りである。
最初に上手くいかない方針を述べる。1がどこにあっても、左端に寄せることは可能である。
よって一気に左に寄せる方法を考える。全区間のちょうど半分ずつ、
同じことがすべての
この方法は公式解説2,3とだいたい同じである。
コードはこちら
青diffなのにE問題より簡単である。相変わらず、正解者数と自分にとっての難易度に連動性がない。
ある障害物の鎖
-
$G$ が上から下まで詰まっている。つまり障害物が取り得る行番号が$H$ 種類ある -
$G$ が左から右まで詰まっている。つまり障害物が取り得る列番号が$W$ 種類ある -
$(1,1)$ 付近を上から左まで覆っている。障害物が取り得る行番号が1から昇順で連番、障害物が取り得る列番号が1から昇順で連番である。 -
$(H,W)$ 付近を下から右まで覆っている。障害物が取り得る行番号が$H$ から降順で連番、障害物が取り得る列番号が$W$ から降順で連番である。
公式解説の補足とだいたい同じ解き方である。
コードはこちら
コードはこちら
問題文通り実装する。文字列が長すぎるときは、文字列を構成する前に打ち切る。
コードはこちら
10進数で12桁以下の回文数を全列挙する。その中で
10進数で12桁以下の回文は、10進数で6桁以下の数字の文字列表記 0 ではない。
-
$S$ が1桁なら$S$ 自身 -
$S$ の反転$R$ について$SR$ -
$S$ が2桁以上なら、$S$ の反転から最後の桁を除いた$R$ について$SR$
コードはこちら
相変わらず問題の意味を取り違える癖が治らない。
家の間隔の集合を昇順に
コードはこちら
公式解説2のようなことを考えて、結局分からず諦めた。公式解説1は理解できていない。
コードはこちら
問題文通り実装する。
コードはこちら
問題文通り実装する。
コードはこちら
解法はすぐ思いつくが、添え字の扱いが難しい。
薬品が空の時は常に安全で、このことは
薬品の状態 0 か 1 かに対応する。ここがややこしい。
薬品の状態
Yes 、そうでなければ No を出力する。
コードはこちら
入力を
ある
コードはこちら
すべての経路について、あるマスまでの最小累積コイン数(負になりうる)
正解はゴールからスタートに向かって、必要な余力をDPすることだった。
コードはこちら
D問題とE問題を解く前に、F問題が解けてしまった。どうして相変わらず、正解者数と私にとっての難易度に連動性がないのだろう。
いかにも遅延セグメント木である。遅延セグメント木
- ノードの文字列幅
$W$ - ノードの最左文字
$C_l$ - ノードの最右文字
$C_r$ - ノードの最左から連続する文字長
$L_l$ - ノードの最右から連続する文字長
$L_r$ - ノードで同じ文字が連続する最大長
$M$ - 単位元は空文字列
作用素は、以下のようにする。
- 文字
$C$ を持つか持たないかいずれかにする - 文字
$C$ を持たないなら何もしない。これは単位元というか恒等変換である。 - ノードに対する作用は、ノードの文字列幅
$W=1$ なら、文字を$C$ に置き換える。それ以外は何もしない。 - 作用素の合成は、文字
$C$ があれば置き換える後勝ちにする
ノード
$U.W = A.W + B.W$ $U.C_l = A.C_l$ $U.C_r = B.C_r$ -
$U.L_l$ は仮に$U.L_l = A.L_l$ とする -
$U.L_r$ は仮に$U.L_r = B.L_r$ とする -
$U.M$ は、$A.M$ ,$B.M$ が候補になる。下記も考慮した、候補の最大値にする。 -
$A.C_r = B.C_l$ なら以下を考慮する-
$U.L_l$ は、$A$ が全部$C_l$ ならくっつける。つまり$A.W = A.L_l$ なら$U.L_l = A.L_l + B.L_l$ である。 -
$U.L_r$ は、$B$ が全部$C_r$ ならくっつける。つまり$B.W = B.L_r$ なら$U.L_r = A.L_r + B.L_r$ である。 -
$A.C_r$ と$B.C_l$ をくっつけた文字列の長さ$A.L_r + B.L_l$ も$U.M$ の候補にする
-
クエリ1は単一ノードを書き換える。クエリ2は
遅延セグメント木ではなく、普通のセグメント木でよかった 。遅延セグメント木だと作用素の設計がめんどくさい。
コードはこちら
0-based indexingに気を付けて数える。
コードはこちら
連続する . のうち、どれか一つだけ o に変える。最も左にすると実装が簡単である。
コードはこちら
コードはこちら
よくわからないWAをして、よくわからないままACした。
答えについて
これは
-
$B$ が$b$ 以上の値を含むときは、$b$ 以上の値の最小値 -
$B$ が$b$ 以上の値を含まないときは、$B$ の最大値
を
コードはこちら
超頂点というヒントを見てしまったのでその通り実装したのだが、WAの山が返ってくる。解けないまま公式解説を読んでも、やはり超頂点だった。多重辺の最短距離を取るのも、上空を経由する方法が2通りあるのも、他の方のコードをみるまで分からなかった。
コードはこちら
s.substr(a, n - a - b) を出力する。
コードはこちら
std::multiset で持つ。
コードはこちら
std::multiset を使ったらTLEした。 std::map を使う。
-
$m = i - A_i$ が$M$ にあれば1減らす。ただし0未満にはしない。 -
$p = i + A_i$ が$M$ に何個あるかを答えに加算する
コードはこちら
プレゼントを末尾から逆再生しながら、あるテンションになる元を探す、明らかに大きすぎるテンションは単調減少、と思ったがそれ以上考えがまとまらなかった。
諦めて公式解説を読み、公式解説をC++17で書き直しただけのコードが上記である。
コードはこちら
D問題より先に解けてしまった。確かにD問題よりも正解者数は多いが。
頂点番号の小さい順にDFSなのだが、そのまま実装するとTLEする。二段階のDFSが要る。
一段階目は、頂点
二段階目は、頂点
コードはこちら
D問題より先に解けてしまった。D問題よりも正解者数は少ないのに。
変数を一か所typoしていることに気が付かず、時間が掛かってしまった。如何にも遅延セグメント木である。遅延セグメント木
-
$T$ の$i$ 番目の要素$T_i$ は、区間の幅と平均値にする。ただし平均値はstd::optional<ModInt>として、幅0の時は値を定義しない。 - 区間を合併するときは、区間の加重平均を取る。ただし幅0の区間は対になる区間の値そのままにする。
- 作用素は
std::optional<ModInt>とする。作用は後勝ち、つまり後から作用して最初に値が定義されている作用素を使う - 初期値は
$T_i = A_i$ でtree.set(i,a)する -
$[L,R]$ の平均値$avg$ はtree.prod(l, r)で求める。これを区間$[L,R]$ にインラインで作用する。tree.apply(l, r, avg)を使う。
最終的な
コードはこちら
コードはこちら
初期状態で行と列は
まだ使える行と列について 行 -1 である。
コードはこちら
方針はすぐ立ったのに、詰めが甘くペナルティを食った。
頂点
ある
-
$j = S$ または$j = T$ なら答えは1である - 往復で
$C = D(S,j) + D(T,j) - D(S,T)$ だけ寄り道することができる。特に$j$ が$D(S,T)$ となる最短経路上なら$C = 0$ である。寄り道上にある点は題意を満たし、寄り道の両端の点を含むので答えは$1 + C / 2$ である。
コードはこちら
if文で判定する。
コードはこちら
連想配列を作る。
コードはこちら
- 時刻
$i$ に乗ったときは、$DP[i+T][j+1] = min(DP[i+T][j+1], DP[i][j] + \sum_i^{i+T-1} P_i)$ 。$\sum$ の部分は累積和を求めておく。 - 時刻
$i$ に乗らなかったときは、$DP[i+T][j] = min(DP[i+T][j], DP[i][j])$
答えは
コードはこちら
ダイクストラ法っぽいが、TLEを連発した。
イベントを早い順に並べ替え、
初期値を
すべてのイベントの組
コードはこちら
コードはこちら
スキル
あるスキル
コードはこちら
包除原理である。
椅子が空いていれば、来た人を座らせても答えは変わらない。なぜなら椅子に座らせずにそのまま会場に案内しても、いったん椅子に座らせてぎりぎりまで会場に案内しなくて、整理券に書かれた番号が小さい順に
1番目の人を案内できないのは、
1番目の人を案内できて、2番目の人を案内できないのは、2番目の人が
同様に
この和を
想定解法は包除原理ではなかった。このエレガントさはARCっぽい。
コードはこちら
まず
値の昇順に並べた
初日から特訓を開始して、平均体力消費量 -
二日目以降の
この解法は決勝の優勝者と同じである(minとmaxの違い)。想定解法はLISっぽいが、座標圧縮とセグメント木は要らないらしい。
コードはこちら
No である。これを検出したらすぐに処理を打ち切らないとREする。
このような組がなければ答えは Yes である。
最大公約数を使おうとして1ペナ、ここで3秒制限なら約数列挙だろうと気づき、REしてもう1ペナもらった。厳密な証明は公式解説通りで、約数ではなく素因数だけ考えて差し支えない。
頂点倍化だと思ったが、公式解説2を読む限り幾重の考察が必要だった。これは自力では思いつかない。
コードはこちら
std::string::rfind("tea") + 3 が tea である。念のため、位置が std::string::npos かそれ以外か調べる。
コードはこちら
総当たりのはずがなぜかWAした。
コードはこちら
水平スキャンだと分かったが手こずった。
値
- ティーバックを1個選ぶためには、
$x = 1$ とする。これを$S[1] = 1$ とする - ティーバックを
$j = 1..a$ 個選ぶためには、$S[1..a] = 1 + N \times (j - 1)$ とする
- 上記の通り
$N$ 種類のティーバックを$C[a_1]$ 個選ぶ。これを$S$ について累積して、$S[1..a_1]$ とする。 -
$N - C[a_1]$ 種類のティーバックを$C[a_2]$ 個選ぶ。これを$S$ について同様にする。 -
$N - C[\sum_{1}^{i-1} a_i]$ 種類のティーバックを$C[a_i]$ 個選ぶ。$S$ について同様にする。
-1 、そうでなければ
コードはこちら
DPで畳み込む。
-
$S_i = 0$ なら、$DP[i][0] = DP[i-1][1] + 1$ ,$DP[i][1] = DP[i-1][0]$ -
$S_i = 1$ なら、$DP[i][0] = DP[i-1][0]$ ,$DP[i][1] = DP[i-1][1] + 1$
答えは
コードはこちら
C++で3370 msecもかかる。
広義の台形の数 - 広義の平行四辺形の数の半分、が答えである。広義の台形を構成する頂点を列挙するとTLEする。
辺の方向
ある方向
$|u| = |v|$ $|P_{u0}-P_{v0}| = |P_{u1}-P_{v1}| \lor |P_{u0}-P_{v1}| = |P_{u1}-P_{v0}|$
コードはこちら
あらかじめ連想配列というか辞書を作る。
コードはこちら
昇順の優先度キューに入れる。
コードはこちら
問題を誤読し、全員の移動回数の和を1ノルムで求めるのかと思ったら全然違った。中央値には違いなかったが。
行について、
よって答えは、
コードはこちら
C問題よりずっと簡単だった。
コードはこちら
不変量っぽい。
DPの遷移元を
コードはこちら
コードはこちら
コードはこちら
クエリに対して、答えを差分更新する。
コードはこちら
着想は早いが実装が遅い。
頂点倍化である。表面
-
.Soからは表面で隣接する.SGo?に行ける。実は行った先が#なら行き止まりなので行っても差し支えない(以下同様)。 -
.Sxからは裏面で隣接する.SGx?に行ける -
?からは、同座標の表面と裏面同士を行き来できる。正確には?とは反対面にある?に隣接するマスに行ける。
辺を張ったら、スタート地点から優先度キューで探索して、スタート地点からの最短距離を求める。表面または裏面のゴールまでの最短距離が答えである。
コードはこちら
着想は早いが実装が遅い。
辺が減らないので如何にもunion-find木である。連結成分の代表元に、黒い頂点の数を持たせる。頂点
- クエリ1は、
$u,v$ が連結なら何もしない。非連結なら連結し、$L$ を連結後の$u,v$ の代表元として、$B[L] = B[u] + B[v]$ で更新する。 - クエリ2は、頂点
$v$ の色を$C[v]$ からを$\overline{C[v]} = 1 - C[v]$ に変えるとする。さらに$B[L[v]]$ に$\overline{C[v]} - C[v]$ を足す - クエリ3は、
$B[L[v]]$ が正の数ならYes、0ならNoである。
コードはこちら
ABC 420-Gは、両辺を二乗した後の式変形が分からず、
コードはこちら
コードはこちら
問題文通り実装する。
コードはこちら
バブルソートして定位置にするしかない。定位置とは ABAB... または BABA.. のどちらかである。
定位置における先頭の A は1番目か2番目である。これを
コードはこちら
着想は早いが実装が遅い。
- あるランを終えた後の移動回数について累積和を取る。つまり
$i$ 回移動した後で、$CS[i] = \sum_{j=1}^{i} S_j$ $CT[i] = \sum_{j=1}^{i} T_j$ 回移動したとする。 -
$CS, CT$ について、重複無し集合$U$ を求める -
$U$ の昇順に、ランをリプレイする。$U[j], U[j+1] , j+1 \leq |U|$ を区間とするランを作り、そのランは$S, T$ の差分である。具体的には以下のように求める。便宜上$U[0] = 0$ ,$V[0]$ の移動回数を0とする。-
$S$ で注目しているランを$p = 1..M$ とする -
$T$ で注目しているランを$q = 1..L$ とする -
$j$ を差分ラン$V$ の番号とする。このランまでの移動回数の累積和を$U[j]$ とする -
$CS[p] \geq U[j]$ を満たす最小の$p$ まで、$p$ を増やす。つまり$CS[p-1]..CS[p]$ が$U[j]$ を含むようにする。 -
$CT[q] \geq U[j]$ を満たす最小の$q$ まで、$q$ を増やす。つまり$CT[q-1]..CT[q]$ が$U[j]$ を含むようにする。 - 差分ランは
$V[j]$ は、移動回数$V[j] - V[j-1]$ について、移動方向の差分$CS[p] - CT[q]$ からなる
-
差分ラン
- 位置の差を
$R_d, C_d$ とする。初期値は$R_d = R_r - R_a$ ,$C_d = C_t - C_a$ である。 -
$V[i] = (l, r, c)$ とする。これは移動方向の差分$(r,c)$ を$l$ 回繰り返すという意味である。 - 差分がない、つまり
$r = 0 \land c = 0$ のときを考える-
$R_d = 0 \land C_d = 0$ なら、二人は常に行動を共にする。よって答えに$l$ を足す - そうでなければ二人は決して同じ位置に居ない。答えは変わらない。
-
- 二人が同じ位置に居る可能性を求める。
$l$ 回移動後の位置の差を$R_e = R_d + l \times r$ ,$C_e = C_d + l \times c$ とする。- 適宜大小を入れ替えて、
$[R_d + r, R_e]$ ,$[C_d + c, C_e]$ が0を含まなければ、二人は決して同じ位置に居ない -
$r \ne 0$ のとき、$R_e - R_d$ が$r$ の倍数で無ければ、列の差は0にならない。$|r| \in {0,1,2}$ であることに注意する。 -
$c \ne 0$ のとき、$C_e - C_d$ が$c$ の倍数で無ければ、行の差は0にならない
- 適宜大小を入れ替えて、
- 上記でなければ、二人が同じ位置に居る可能性が高々1回ありうる
-
$r = 0$ のとき、$C_e - C_d$ が$c$ の倍数なら、二人は1回同じ位置に居る -
$c = 0$ のとき、$R_e - R_d$ が$r$ の倍数なら、二人は1回同じ位置に居る - そうでなければ、
$C_d + c(-R_d / r) = 0$ のとき、二人は1回同じ位置に居る
-
コードはこちら
遅延セグ木という言葉がちらっと見えてしまったので、純粋な自力ACかというと割引が要る。でもまあ自力AC扱いしておく。
クエリを先読みする。クエリ2は削除しないことにして、クエリ1について boost::multi_index_container で std::set 兼 set::list を作ればよい。
using SetAndList = boost::multi_index_container <
Num,
boost::multi_index::indexed_by <
boost::multi_index::ordered_unique <
boost::multi_index::tag<order>,
boost::multi_index::identity<Num>
>,
boost::multi_index::sequenced <boost::multi_index::tag<seq>>
>>;リストを先頭からなぞって、最終的に
- クエリ1は、
$T[P[x]] = x$ とする - クエリ2は、
$L = P[x]$ ,$R = P[x]$ として、$T[(L+1)..(R-1)]$ の区間和を出力する ($L > R$ なら$L,R$ を入れ替える)。その後、$T[(L+1)..(R-1)]$ を0に更新する。
boost::multi_index_container を使っても1018 msecで間に合う。
コードはこちら
問題文通り桁繰り上がりをする。
コードはこちら
端に注意して、問題文通り実装する。
コードはこちら
ARC 201-Aの屈辱を思い出し、同様に解いたら間違えた。
コードはこちら
0 4 2 6 1 5 3 7 である。この配列は、マージソートの要領で作ることができる。
- 初期行列を
$P$ とする -
$P$ と$P+|P|$ をzipして、新たな$P$ を作る - これを
$N$ 回繰り返すと$W = 2^N$ になる
このようにした求めた
上記は葉から構築したが、根から構築することも できる 。区間
コードはこちら
ABCでも乱択アルゴリズムが出る。
決定的アルゴリズムが全く分からず、乱択アルゴリズムで押し切ったら、実は乱択アルゴリズムが想定解だった。
任意の二点を通る直線 No である。
コードはこちら
コードはこちら
左端から行ける最右の部屋を
コードはこちら
解法は見えたのに実装で手こずった。
センチネルを置いて、
-
$R$ から$left$ まで、閉まっている扉を開ける -
$left$ から$R$ まで、扉を全部閉める -
$R+1$ から$right$ まで、閉まっている扉を開ける -
$right$ から左に向かって、開いている扉を閉める。閉まっている扉を見つけたら終了する
コードはこちら
C問題と同様のシミュレーションだが、実装で手こずった。
店の容量が足りないときは、誰かが退店するまで必ず待たされる。これを利用して上手く時計を進める。
- 店に入るグループの待ち行列
$P$ に要素$(A_{i}, B_{i}, C_{i}, i)$ を$A$ の昇順に入れる - 店から出るグループの待ち行列
$Q$ に要素$(T_{i} + B_{i}, C_{i})$ を$T_{i} + B_{i}$ の昇順に入れる。ここで$T_{i}$ はグループ$i$ が入店した時刻である。
-
$P$ の先頭から空きがある限りグループを入れる。$T = max(T,A)$ ,$S = S - C$ で更新する。 -
$Q$ の先頭からグループを退店させる。$T = max(T,A)$ ,$S = S + C$ で更新する。
コードはこちら
累積和で求めようしたが、全く答えが分からなかった。公式解説2をそのまま実装したがやはり分からない。
メビウス変換ってなんでしょう。
コードはこちら
Yes 、そうでなければ No である。
コードはこちら
正解した問題の番号を集合で管理して、正解した数を数える。
コードはこちら
トポロジカルソートしようとして思いとどまった。
依存関係をグラフにして、先行条件なしのスキルからBFSする。
コードはこちら
DFSで全探索する。要領よく実装しないとTLEする。Bit DPという言葉がちらっと見えたが、自力AC扱いにしておく。
- 解の最大値は
$HW$ で、実際にはもっと少ない(白は4マスに一つ置けばいい、公式解説を参照)。よって既知の最小解を共有し、それ以上白く塗ったら枝刈りして探索を打ち切ってよい。 -
$(R,C)$ が元々白なら、塗らないしか選択肢が無い - ある
$(R,C)$ マスに注目して、$(R-1,C-1),(R-1,C),(R,C-1),(R,C)$ が全部黒なら、$(R,C)$ は白く塗るしか選択肢が無い - そうでなければ塗るか塗らないか両方試す
- BitSetのオフセットは
$RW + C$ より、制約から$2^3W+C$ にする方が速いかもしれない。乗算の代わりにシフト命令を使える。制約から右と下の余分な列はすべて白と仮定して構わない。 - 黒いマスの数は
std::bitset::count()で求まる
解の上限を
コードはこちら
D問題より先に解けてしまった。制約を読み違えてTLEし、方針を立て直してから実装に時間が掛かってしまった。
愚直にシミュレーションするとTLEするので、元々は同じ棒だったものとひとまとめに扱う。
-
$K$ 回分割する。残り操作回数$R = K$ を$(A, C)$ について$C$ ずつ減らしていく - 優先度キューの先頭を
$(A, C)$ とする -
$(A, C)$ を取り除く -
$C \geq R$ なら、未分割分$(A, C - R)$ 、分割分$(A/2, 2R)$ をキューに載せる -
$C < R$ なら、分割分$(A/2, 2C)$ をキューに載せる
先頭
この方法は公式解説3と同じである。
コードはこちら
D問題より先に解けてしまった。セグメント木に載せればよいのは一目瞭然だが、そこからかなり時間が掛かった。
線分
- 区間
$[A,B]$ において、他の線分の出入りの和が0。ここで出を1, 入を-1とする。 - 区間
$[A,B]$ において、他の線分のからの飛び込みが0。線分$[C,D]$ において、$D$ に飛び込み元を$C$ とすると、区間$[0,A)$ からの飛び込みが$[A,B]$ にならない。
1は区間和セグメント木
-
$T[A]$ に1を足す。単位元は0である。$T[B]$ から1を引く。区間和$[A,B]$ が0である。 -
$W[B]$ を$A$ にする。単位元は$\infty$ である。区間最小$[A,B]$ が$A$ 以上である。
ARC 206で緑落ちしたので、ratedで参加した。水色に戻した。
コードはこちら
符号
コードはこちら
最初に集合
-
$A_i$ が$-1$ なら無視する -
$A_i$ が$S$ に含まれるなら$S$ を消費する。つまり$S$ から$A_i$ を除く -
$A_i$ が$S$ に含まれなければ値$A_i$ が重複するので答えはNoである
想定解法は全探索だったらしいが、公式解説2と同じ解き方である。
コードはこちら
いつものカーソルである。
0-based indexingでカーソルを
クエリ2は
-
$L = R$ なら答えは$A_L$ である。入力例に救われた。 -
$L < R$ なら答えは$\sum_{i=L}^R A_i$ である。あらかじめ累積和を求めておく。 -
$L > R$ なら答えは$\sum_{i=0}^R A_i + \sum_{i=L}^{N-1} A_i$ である。
場合分けしなくても、長さ
コードはこちら
答えはすぐ分かったが詰めが甘かった。マスを塗りつぶす操作は一斉に行わなければならない。
最初に条件つまり隣接する黒マスが1個のマスの集合
最後に黒マスを数えて答える。
コードはこちら
5ペナ + 2000 msきっちりでACした。運に助けられた。
制約
最初に std::unordered_map<Num,Num> にする。 std::map<Num,Num> だと間に合わない。
いったん
なので
これで2000 msきっちりでACした。この方法は公式解説3と似ているが、公式解説3は遥かに速い。あらゆる素因数ではなく、
おそらくもっと良い解法があるはずだと思い、公式解説を読んだら多項係数の除算を無くす式変換と、二項係数の漸化式が載っていた。 この方法 は全く思いつかなかった。
コードはこちら
名前をバージョンに対応するテーブルを固定で持つ。
コードはこちら
文字を出現回数に対応するテーブルを作って、出現回数が1の文字を返す。
コードはこちら
尺取り法しようとして単調性が無いので(バージョンの追い越しがある)、遅延セグメント木で解いた。
と思ったら、やはり尺取り法で 解けた 。
コードはこちら
1時間を超えてしまった。
- 寄せる先の位置は
$[P_i, P_i + C_i)$ である -
$P_i$ より左の数をすべて寄せる。どの数も寄せる必要があり、その個数は$P_i - 1$ 回である -
$P_i$ より左$v$ は寄せたあと反転する必要がある。その個数は$\sum_{j=1}^{i-1} C_j$ 回である。これは累積和を求めておく。 - 右から寄せる場合も同様にする
ランレングスは高々
コードはこちら
決定的解法を検討して2時間を超えてしまった。実は三分探索を二回やるだけである。
高橋君は青木君以上に移動時間が掛かるとみなして一般性を失わない。そうでなければ入れ替える。
高橋君の移動時間は
単位時間の移動距離は、高橋君のX方向について
時刻
時刻
念のため時刻
距離の二乗は
コードはこちら
0-based indexing で、
コードはこちら
コードはこちら
コードはこちら
問題文を読み取れなかった。
最初はAliceはどこから出発してもよいと思って解けなかった(出発点は頂点1固定だった)。次に、AliceとBobの手番は合計
解説を読んで問題文を盛大に誤読していると分かった。ここまで分かれば、部分木のうち自分の必勝手があれば選び、なければ他人の勝ちである。
コードはこちら
周期
コードはこちら
総当たりするだけなのだが、異常に時間が掛かった。
コードはこちら
B問題もC問題も時間を掛けすぎである。
差分 ( を1、 ) を-1とする。累積和をセグメント木
- クエリ1に対しては、
$T[L+1] = T[L] + D$ として、その後$L$ に1を足す - クエリ2に対しては、
$L$ を1引く
Yes 、そうでなければ No である。セグメント木に載せて、
コードはこちら
E問題緑diffは半日掛かるのに、水diffは数分で方針が立つ。
これを
コードはこちら
明らかに木DPで解ける問題に半日掛かった。
始めにオイラーツアーで解こうとして解けず、木の直径で解こうとして解けず、1パスの木DPで解けず、2パスの木DPでようやく解けた。
後処理を楽にするため、次数が1の点を根
そこからDFSして、各頂点
次に子に対してDFSで木DPを行う。まず根
この木DPを平たく言えば、
ある頂点
実装上の注意として、
これで全頂点について答えが求まった。実装効率が悪すぎて2秒制限が危なかった。
公式解説には木の直径を用いた解法が載っている。木の直径を満たす二点のどちらかしか答えがないことは直感的には分かった。しかし頂点番号最大を導く方法が分からなかった。確かにこの方法で 正解 するが、注意深い実装が要る。