积木大赛

积木大赛

题解:

考试时写了一个贪心,每次在最高的能放的地方放。虽然没有证明,但似乎可以水过答案,最后得70分,剩下30分为TLE。

我们发现,如果不断垒高积木,最后是一个金字塔的形状。

积木大赛(图片来源网络)

即使最后的积木形状不像一个金字塔,但它一定包含了金字塔的一部分。

观察上图,灰色块为原块,红色块为假象块,绿色块为我们要添加的块。要求得绿色部分的方块数,

可以用大金字塔的方块数 - 黄色三角形方块数 - 紫色三角形方块数 - 棕色区域方块数,设大金字塔的高为X,黄三角形坐标为L,紫三角形坐标为R。

则大金字塔的方块数 = X * X  黄色三角形方块数 = A *(A+1)/2(等差数列)紫色三角形方块数=B *( B+1)/2          棕色区域方块数可以用前缀和算出,其中三角形边长A = 二分高度 - (当前高度 - L),B同理。

然后二分答案。对于一个高度,我们检查用给定数量的方块能否达到那个高度。首先预处理出合法的L和R,可以发现H[L]具有单调性,从n枚举到1,H[L] < 二分高度 - (枚举X - L)时缩小L,满足条件时保存下来。

然后再1到n枚举X,计算出所用的方块,与m比较,若小于m则返回true。

代码:
 

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<vector>
#include<queue>
#include<cmath>
#define MAXA 100005
using namespace std;
typedef long long LL;
int n,m,Sum,Temp,Ans,MAXH,pos,H[MAXA],Left,Right,Mid,TempL[MAXA],TempR[MAXA];
LL Pre[MAXA];
bool Check(int NowH) {
	int L = n,R = 1;
	for(int i=n;i>=1;i--) {
		while(H[L] < NowH - (i - L) && L >= 1)
			L--;
		TempL[i] = L;
	}
	for(int i=1;i<=n;i++) {
		while(H[R] < NowH - (R - i) && R <= n)
			R++;
		TempR[i] = R;
	}
	for(int i=1;i<=n;i++) {
		if(TempR[i] > n || TempL[i] < 1) continue;
		LL Big = (LL)NowH * (LL)NowH;
		LL TriL = (LL)(NowH - (i - TempL[i]) + 1) * (LL)(NowH - (i - TempL[i])) / (LL)2;
		LL TriR = (LL)(NowH - (TempR[i] - i) + 1) * (LL)(NowH - (TempR[i] - i)) / (LL)2;
		if(Big - TriL - TriR - (Pre[TempR[i]-1] - Pre[TempL[i]]) <= (LL)m) return true;
	}
	return false;
}
int main() {
//	freopen("block.in","r",stdin);
//	freopen("block.out","w",stdout);
	scanf("%d %d",&n,&m);
	for(int i=1;i<=n;i++) {
		scanf("%d",&H[i]);
		MAXH = max(MAXH,H[i]);
		Pre[i] = Pre[i-1] + 1LL * H[i];
	}
	Left = MAXH + 1,Right = (int)(sqrt(m) + 1.00) + MAXH + 1;
	while(Left < Right) {
		Mid = (Left + Right) / 2;
		if(Check(Mid)) {
			Left = Mid + 1;
		}
		else Right = Mid;
	}
	printf("%d",Left - 1);
}