[題解](枚舉/二分/單調隊列)積木大賽(考試)


2.积木大赛

(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]必须要有积木)。

  

  如果搭的积木大厦越高,平平同学就会觉得越舒服,现有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 


根據題意,所形成的應該是一個嚴格的金字塔形狀,但是可能兩邊不完整,因為如果原本就有的高度超過所需高度就不需要在往左/右搭建剩餘的部分了,如樣例

 首先想到枚舉所有點,然後發現高度顯然具有單調性,所以可以二分在枚舉

但對於每個點都需要O(n)的找它的左右邊界,複雜度n*n*log(n)

然而每次都找左右邊界太浪費時間,所以想辦法優化

其實這個高度是滿足單調性的,例如當從某點移動到它左邊的點時,它的左邊界是單調不增(不向右移動)的,這樣就可以單調隊列搞一下

直接以O(n)的時間預處理了所有點的左右邊界,複雜度O(nlog(n))

#include<iostream>
#include<cstdio>
#include<cmath>
#define ll long long
using namespace std;
const int maxn=100009;
int n,m,h[maxn],maxx;
long long sum[maxn];
int L[maxn],R[maxn];//i点的左边界和右边界位置 
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--;//x-(i-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);
//        long long y=x*x-((b+1)*b/2)-(a+1)*a/2;
//        if(y-(sum[R[i]-1]-sum[L[i]])<=m)return 1;
        ll y=(ll)x*(ll)x-(ll)(b+1)*(ll)b/2ll-(ll)(a+1)*(ll)a/2ll;
        if(y-(ll)(sum[R[i]-1]-sum[L[i]])<=(ll)m)return true;
    }
    return 0;
}
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)scanf("%d",&h[i]),sum[i]=sum[i-1]+h[i],maxx=max(maxx,h[i]);
    int l=maxx+1,r=(int)(sqrt(m)+1.00)+maxx+1;
    while(l<r){
        int mid=l+r>>1;
        if(check(mid))l=mid+1;
        else r=mid;
    }
    printf("%d",l-1);
}
原文地址:https://www.cnblogs.com/superminivan/p/10957103.html