地味に場合分けの面倒な問題。
https://yukicoder.me/problems/no/1440
問題
以下のクイズ大会を考える。
- 初期値で参加者全員の持ち点は0ポイント
- 正答すると、その人の持ち点は1ポイント増加
- 誤答すると、その人の誤答がD回目の場合、持ち点はDポイント減少(持ち点は負にもなりうる)
- 最終的に自身のポイントより高い人がR人いると、その人は(R+1)位となる。
N人の参加者がいて、正答がA回、誤答がW回の場合、K位の人の取りえる最高ポイントは何ポイントか。
K位が存在しない場合はそう答えよ。
解法
細かく場合分けしていく。
- まずK位を作れない場合を考える。
- Aが(K-1)未満の場合、K位より上の人を全員正の点数にすることができない。一方Wが(N-(K-1))未満の場合、K位以下を全員負にすることもできないので、これらは自明にK位が存在しない。
- KがNより小さい場合
- 誤答はK位以下に押し付ければよい。上位(K-1)人よりK位が1ポイントだけ小さい状況を作りたいのでfloor*1/K)が解
- KがNと等しい場合
- まずWがNの倍数である場合を考える。誤答はまず全員で割り振ろう。あとは(K<N)のケースのように正答を割り振るわけだが、Aが(K-1)未満の場合は割り振り切れないので、K位だけ誤答を1回増やさせ、その分正答を充当する。
- WがNの倍数でない場合、1回多く誤答を割り振られた人の中で、上と同様のケースを考える。
int T; ll N,A,W,K; void solve() { int i,j,k,l,r,x,y; string s; cin>>T; while(T--) { cin>>N>>A>>W>>K; if(K-1>A&&N-K+1>W) { cout<<":("<<endl; } else if(K<N) { if(A>=K-1) { cout<<(A-(K-1))/K<<endl; } else { cout<<-1<<endl; } } else { if(W%N==0) { ll P=-(W/N)*(W/N+1)/2; if(A>=K-1) { P+=(A-(K-1))/K; } else { P+=min(-1LL,-(W/N+1)+A); } cout<<P<<endl; } else { ll P=-(W/N)*(W/N+1)/2; ll Q=-((W/N)+1)*(W/N+2)/2; int dif=W%N; if(abs(P-Q)*dif<=A) { A-=abs(P-Q)*dif; if(A>=K-1) { P+=(A-(K-1))/K; } else { P--; } } else { if(A>=dif-1) { P=Q+(A-(dif-1))/dif; } else { P=Q+min(-(W/N+2)+A,-1LL); } } cout<<P<<endl; } } } }
まとめ
★2はきついよね。
*1:A-(K-1