zoukankan      html  css  js  c++  java
  • Gym

    题意: 一个宽度为N的网格图,i上有h[i]高的方块。现在你有W个方块,问怎么放使得最终的最高点最高。

         当一个格子的下方,左下方和右下方都有方块那么久可以把方块放到这个格子上。最左端和最右端不能放方块。

       (N<=100000,W<=1018,h[i]<=109

    思路:显然是二分,对于二分的高度Mid,我们验证是否有i能够达到这个高度。我们已知需要填充的部分是从i向两旁延伸,知道左边满足i-pos>=Mid-h[pos]

    右边满足pos-i>=Mid-h[pos],我们需要对于每个i找到Lpos和Rpos,然后用前缀和O(1)算出需要的方块。

    现在的关键就是对于Mid,O(N)内预处理得到L和R数组:对于pos,它影响的范围是pos+Mid-h[pos]及其以后,那么记录下L[pos+Mid-h[pos]]=pos,然后扫描更新一遍最大值即可;R数组同理。

    (emmm,我个zz,果然还是太弱。

    #include<bits/stdc++.h>
    #define ll long long
    using namespace std;
    const int maxn=100010;
    int N,L[maxn],R[maxn]; ll sum[maxn],h[maxn],W;
    bool check(ll Mid)
    {
        int i;
        
        for(i=1;i<=N+1;i++) L[i]=0,R[i]=N+1;
        for(i=1;i<=N;i++) if(i+Mid-h[i]<=N&&i+Mid-h[i]>=i) L[i+Mid-h[i]]=i;
        for(i=N;i>=1;i--) if(i-Mid+h[i]>=1&&i-Mid+h[i]<=i) R[i-Mid+h[i]]=i; //方向不能反,因为要最近的 
        for(i=1;i<=N;i++) L[i]=max(L[i],L[i-1]);
        for(i=N;i>=1;i--) R[i]=min(R[i],R[i+1]);
        for(i=1;i<=N;i++){
            if(R[i]==N+1||L[i]==0) continue;
            ll res=0;
            res+=1LL*(Mid+Mid-(i-L[i])+1)*(i-L[i])/2;
            res+=1LL*(Mid-1+Mid-1-(R[i]-i-1)+1)*(R[i]-i-1)/2;
            res-=sum[R[i]-1]-sum[L[i]];
            if(res<=W) return true;
        }
        return false;
    }
    int main()
    {
        scanf("%d%I64d",&N,&W);
        ll l=0,r=2000000000,Mid,ans;
        for(int i=1;i<=N;i++) scanf("%I64d",&h[i]),l=max(l,h[i]),sum[i]=sum[i-1]+h[i];
        while(l<=r){
            Mid=(l+r)/2;
            if(check(Mid)) l=Mid+1,ans=Mid;
            else r=Mid-1;
        }
        printf("%I64d
    ",ans);
        return 0; 
    }
  • 相关阅读:
    Git
    Spring
    Linux
    Linux
    Linux
    Linux
    Linux
    Linux
    Linux
    Linux
  • 原文地址:https://www.cnblogs.com/hua-dong/p/9470853.html
Copyright © 2011-2022 走看看