kmjp's blog

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

TopCoderOpen 2013 Round1C Medium TheOlympiadInInformatics

さてMedium。
ちょっと手こずったけどうまく解けて良かった。
http://community.topcoder.com/stat?c=problem_statement&pm=12456

問題

N個の部屋があり、それぞれに何人かの受験生がいる。
各部屋の受験生の得点の合計値がわかっており、かつ自分より点数が高い人を確実にK人以下にしたい場合、自分が最少何点取ればよいかを答える。

解法

自分がA点取った場合、自分より点数が高い人が最大になるのは(A+1)点の受験生が多数いる場合。
よって、各部屋の得点の総和を(A+1)で割れば、その部屋で自分より高い得点の最大人数がわかる。

自分より得点の高い人の総和は自分の得点に対し単調減少なので、後は2分探索で最適解を探せばよい。
整数値の2分探索なので、最後挟み込む2つの値の差が1になったとき注意。

class TheOlympiadInInformatics {
	int N,K;
	vector <int> S;
	public:
	
	int enough(ll num){
		int i;
		ll n=0;
		FOR(i,N) n+=S[i]/(num+1);
		return n<=K;
	}
	
	int find(vector <int> sums, int k) {
		S=sums;
		N=S.size();
		K=k;
		
		int i;
		ll l=0,r=1LL<<40;
		
		if(enough(0)) return 0;
		
		FOR(i,50) {
			if(l==r) return (int) l;
			if(l+1==r) {
				if(enough(r)) return r;
				return l;
			}
			else {
				ll pivot=(l+r)/2;
				if(enough(pivot)) r=pivot;
				else l=pivot;
			}
		}
		return 0;
	}

};

まとめ

これはさらっと2分探索が思い浮かべられて良かった。