kmjp's blog

競技プログラミング参加記です

yukicoder : No.1440 The Quiz Competition

地味に場合分けの面倒な問題。
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