zoukankan      html  css  js  c++  java
  • [題解](枚舉/二分/單調隊列)積木大賽(考試)


    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);
    }
  • 相关阅读:
    [060428]事件中的过去时和进行时,没想到是这种区别。
    4月11日,string这差距怎么就这么大呢
    暴一个vs2005的bug
    membership,想说爱你不容易
    4月5日,遇到一个GridView很头疼的问题
    ActiveX控件的打包发布[无证书发布]
    3月14号,oracle的说道多多
    [转载]在SQL Server数据库之间进行数据导入导出,OPENDATASOURCE Virus
    U盘加载,卸载,拔出,插入,WM_DEVICECHANGE,WndProc,DBT_DEVICEARRIVAL,DBT_DEVICEREMOVECOMPLETE Virus
    C#,String.Format,数字格式化输出 ,format Virus
  • 原文地址:https://www.cnblogs.com/superminivan/p/10957103.html
Copyright © 2011-2022 走看看