1. 程式人生 > >CodeForces 985D Sand Fortress (二分)

CodeForces 985D Sand Fortress (二分)

題目連結

題意:給你n個沙包和一個h(後面又說h的用處),要求堆一面沙牆(這面沙牆由很多高矮不一的沙堆組成,沙堆的高度由沙袋數量決定,即一個二維問題),求最少的沙堆數量建出題目要求的沙牆。

沙牆要求:1.第一根沙堆的高度不能超過h ; 

                 2.任意兩根沙堆的高度差不能超過1(即最後一個沙堆高度一定為1) ; 

                 3.沙牆的所有沙堆消耗的沙袋總數為n

題解:從題目意思可以發現這裡分為3種情況,

          第一種是這麼堆:K(K<=h),K-1,K-2,...,2,1  =  n  ;

          第二種

是這麼堆:K(K>h),K+1,...,K+x-1,K+x,K+x-1,...,K,K-1,...,2,1 = n;

          第三種是這麼堆:K(K>h),K+1,...,K+x-1,K+x,K+x,K+x-1,...,K,K-1,...,2,1 = n;

          由於考慮到要找最少的堆數目,我們可以使用二分答案的辦法來解決,二分縮小範圍的原理是堆mid個沙堆所能堆出的沙牆使用最多的沙袋的數目是否大於題目給出的n,若是有說明我們可以嘗試縮小範圍,得到更小堆得數目從而求得題目要的最小值。

          至於二分的範圍由數學的等差數列求和公式知道,R_max=2*sqrt(1e18) 左右 ,別取太大任意在乘法運算爆longlong,還有除2,可以用等式另一邊乘2代替。

程式碼如下:

#include<iostream>
#include<cmath>
#include<string>
#include<cstring>
#include<cstdio>
#include<time.h>
#include<algorithm>
#include<vector>
#include<map>
using namespace std;
#define ll long long
#define inf 0x3f3f3f3f
const int maxn = 1e5 + 500;
ll n, h;
bool ok(ll x) {//這裡判斷求得的是長度為x的沙牆最多能堆放的沙包個數是否大於n
	if (x <= h) //x+(x-1)+...+3+2+1
		return x *(x + 1) >= 2*n ? true : false;
	else {//h+(h+1)+...+(h+k-1)+(h+k)+...+(h+1)+h+(h-1)+...+2+1 或 h+(h+1)+...+(h+k-1)+(h+k)+(h+k-1)+...+h+...+2+1
		ll t = x - h;
		if (t % 2)
			return (1 + h + t / 2)*(h + t / 2) + (h + h + t / 2)*(t / 2 + 1) >= 2 * n ? true : false;
		else
			return (1 + h + t / 2)*(h + t / 2) + (h + h + t / 2 - 1)*t / 2 >= 2 * n ? true : false;
	}
}
int main() {
	while (~scanf("%lld%lld", &n, &h)) {
		ll l = 1,r = 2*inf;
		while (l<=r-1){//二分ans
			ll mid = (l + r) >> 1;
			if (ok(mid))
				r = mid;
			else
				l = mid+1;
		}
		cout << l << endl;
	}
	return 0;
}