1.积木大赛

(block.pas/c/cpp)

【问题描述】

为了庆祝国庆,厦门一中举办了一年一度的“积木大赛”。

在2013年NOIP大赛中,夏夏同学己经搭建了宽度为n的大厦,其中第i块高度为hi。今年比赛的内容是对其NOIP2013搭建大厦进行扩建,使用的材料也都是体积为1正方体积木。

今年搭建的规则是:如果要在某一个位置上放一个积木,必须满足它的左下、下方、右下都有积木(用二维坐标a表示,如果要在a[i,j]位置放积木,那么a[i-1,j-1]、a[i,j-1]、a[i+1,j-1]必须要有积木)。

 20171001四校联考

  如果搭的积木大厦越高,夏夏同学就会觉得越有成就感,现有m个积木,问你能搭建的最大高度是多少?

【输入】

第一行两个用空格隔开的整数n和m,分别表示己搭好的宽度和可以使用的积木数量。

后面有n行,每行一个整数hi表示己搭建的第i列积木的高度。

【输出】

一个整数,表示能搭建的最大高度。

【输入输出样例】

样例1

样例2

block.in

block.out

block.in

block.out

8 4

3

4

2

1

3

3

2

4

5

3 100

3

3

3

4

【数据说明】

 30%的数据满足:n<=10;m<=1000。

 50%的数据满足:n<=100;m<=1000,000。

 70%的数据满足:n<=1000;m<=10,000,000。

 80%的数据满足:n<=10,000;m<=100,000,000。

 100%的数据满足:n<=100,000;m<=1000,000,000;1<=hi<=100000。

二分答案+双指针扫描

二分搭建的高度,最低为maxh+1,最高为maxh+sqrt(m)+1

如何在O(n)时间内check呢?

对于搭建积木,我们要搭出一个金字塔形,但是,并不是要搭建整个金字塔形,有时候只要搭建部分即可,如图:

20171001四校联考

我们仅需搭建绿色部分,而不需要搭建蓝色方框内的整个金字塔

如何找到绿色部分呢?

设l为绿色部分的左边界,r为绿色部分的右边界,x为当前要搭建的金字塔顶,h为大金字塔塔高

我们需要在搭建的金字塔所需积木=整个大金字塔所需积木-黄色部分所需积木-紫色部分所需积木-棕色部分所需积木

设黄色部分高为a,则a=h-(x-l)(即图中6-1=5),黄色部分所需积木为a*(a+1)/2。

同理,能算出紫色部分所需积木。

棕色部分所需积木=r前面所有已有积木-l前面所有已有积木。我们想到了什么?前缀和

这样,绿色部分所需积木就算出来了,我们比较它与m的大小关系即可

等等!还没说l和r怎么求呢?

我们发现,对于一个位置x,如果能找到它所对的l,那么对于位置x-1,它的l必≤位置x所对的l,且从位置x所对的l ~x-1绝对不满足能做绿色部分的左边框。因为在向左移的过程中,左边的每个位置的h的要求总是增大的(金字塔形)

所以l具有单调性。我们从n到1扫描每个位置,如果当前的l不满足h[l]<h-(i-l)(即不能做绿色部分的左边框),那么我们l--,直到满足要求,然后我们拿一个数组记下每个位置的l

20171001四校联考

对r也是一样,只要从左往右扫描即可

注意:前缀和要开long long,二分答案的下限一定要从maxh+1开始(不然会引发l==r==i或l>i或r<i等奇奇怪怪的事故),l和r不能小于1或大于n(超出给定范围n外的位置不能放积木)

#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
typedef long long ll;
int h[100001],Max=0;
long long bb[100001];int n,m;
int L[100001],R[100001];
bool check(int x)
{
    int l=n,r=1;
    for(int i=n;i>=1;i--){while(h[l]<x-(i-l)&&l>=1)l--;L[i]=l;}
    for(int i=1;i<=n;i++){while(h[r]<x-(r-i)&&r<=n)r++;R[i]=r;}
    for(int i=1;i<=n;i++)
    {
        if(R[i]==n+1||L[i]==0)continue;
        int a=x-(i-L[i]),b=x-(R[i]-i);
        ll y=(ll)x*(ll)x-(ll)(b+1)*(ll)b/2ll-(ll)(a+1)*(ll)a/2ll;
        if(y-(ll)(bb[R[i]-1]-bb[L[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]);Max=max(Max,h[i]);bb[i]=bb[i-1]+(ll)h[i];
    }
    int l=Max+1,r=(int)(sqrt(m)+1.00)+Max+1;
    while(l<r)
    {
        int mid=(l+r)/2;
        if(check(mid))l=mid+1;
        else r=mid;
    }
    cout<<l-1;
    return 0;
}
View Code

相关文章: