ABC143 バチャ メモ
atcoder.jp
これは単なる生活崩壊なのじゃ
A
max(0, A - 2*B) だねB
D[i] * D[j] をたしますC
連結している奴をひとつとみればいい左から見て一つ着目して同じだったらずらしていくっていう方針で良いんじゃないかな
D
普通にやると なので、予め大きさでソートしておけば2つ決めた時にlower_boundで何個作れるかわかるので になるE
で最短経路なのでまずWarshall Floydをする すると、どこまでが距離 でいけるのかがわかるので、 以下の経路を にして残して、それ以外を消す。その後もう一回 Warshall Floyd をすると、 何回補給すれば良いのかが求まる
F
数字の種類についてこの問題は聞いているので、数字の種類 が何個あるのかというものに変換する。... 数字の種類 が 個ある といった数列を形成する。
そして種類の選び方も任意のため、どの数字だったかによらずにソートしてしまって良い。
さて、 回取る操作を 回操作を行うと仮定する。
この時、 任意の について、 回しか取られない。よって の時は と見ても変わらない。
実は、 であれば、 回以上取ることが出来る。
問題を言い換えると、
さらに、
になるように数字を減らせるからである。
この時、 であるような の数は 以下である。
もしも より多かったら、 となり矛盾するからである。
そのため、 回操作を行った時点で、 のものから一つ取ることで、次のような条件を達成できる。
あとは、 について が単調減少になるかつ を大きい順にソートしておけば、 の処理がならしで でできるので、 各 について高速に動き、
ソートに一番時間がかかり、 でこの問題は解ける。
難しいな
ソース
void Main() { int N; cin >> N; vint A(N); cin >> A; vint cnt(N, 0); for (int i = 0; i < N; i++) cnt[--A[i]]++; sort(rbegin(cnt), rend(cnt)); vint rsum(N); for (int i = N-1; i >= 0; i--) { rsum[i] = cnt[i]; if (i < N-1) rsum[i] += rsum[i+1]; } int t = N; int k = 0; for (int K = 1; K <= N; K++) { while (t > 0) { while (cnt[k] > t) k++; int sum = k * t + rsum[k]; if (sum >= K * t) { cout << t << el; break; } t--; } if (t == 0) cout << 0 << el; } }