kmjp's blog

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

yukicoder : No.2746 Bicolor Pyramid

これは知らなかった。
https://yukicoder.me/problems/no/2746

問題

青いブロックB個、赤いブロックがR個ある。
ここでk段のピラミッドを作ることを考える。
上からk段目はk^2個の同じ色のブロックを置かなければならない。

最大何段のピラミッドを構築できるか。

解法

f(k) = 1^2+2^2+...+k^2とする。

293段以下の場合、
dp(k,n) := k段のピラミッドを作るときに青いブロックをn個ピッタリ使えるか
というdpテーブルを埋めてみる。
dp(k,n)がTrueかつn≦Bかつn+R≦f(k)ならkが解。

294段以上の場合、B+R≦f(k)となるkが解なので、kを二分探索してよい。

ll A,B;
int ok[295][86400];
void solve() {
	int i,j,k,l,r,x,y; string s;
	
	cin>>A>>B;
	if(A>B) swap(A,B);
	if(A>=85344) {
		ll v=0;
		while((v+1000)*(v+1001)*(2*v+2001)/6<=A+B) v+=1000;
		while((v+1)*(v+2)*(2*v+3)/6<=A+B) v++;
		cout<<v<<endl;
	}
	else {
		ok[0][0]=1;
		int cand=0;
		ll v=0;
		FOR(i,293) {
			FOR(j,85400) {
				ok[i+1][j]|=ok[i][j];
				if(j>=(i+1)*(i+1)) ok[i+1][j]|=ok[i][j-(i+1)*(i+1)];
			}
			int num=0;
			FOR(j,85400) if(ok[i+1][j]&&j<=A) cand=max(cand,j);
			if(cand+B>=(i+1)*(i+2)*(2*v+3)/6) v=i+1;
		}
		A=min(A,(ll)cand);
		if(v==293) {
			while((v+1000)*(v+1001)*(2*v+2001)/6<=A+B) v+=1000;
			while((v+1)*(v+2)*(2*v+3)/6<=A+B) v++;
		}
		cout<<v<<endl;
	}
}

まとめ

ある程度以上kが大きくなると自由度が上がるの面白いな。