[HackerRank 101 Hack 51] Small Cubes

题意

给定一个 (n,m,k) 的长方体,沿 x 轴每一层mk个单位1的小正方体,其中,每一层都有一个矩形区域是空的,这个矩形区域是平行于 y,z 轴的。现在用正方体填满这个长方体的空缺部分,用到的正方体的最大边长为 max ,正方体的个数为 num ,最大化 maxp+numq ,p,q给出。 n,m,k105

题解

如果确定了 max ,那么显然边长为 max 的正方体只用一个,其他区域都用边长为1的填充是更优的。同时,如果一个边长为 max 的正方体放的进去, max1 的正方体肯定也能放的进去。如果能求得 max 的最大值就可以通过 O(n) 枚举 max 来求得答案了。
那么如何求得 max 的最大值呢?根据 max 合法的单调性,可以考虑二分。由于 n105 ,应当考虑 O(n) 的检验使得复杂度为 O(nlogn)
如何进行 O(n) 检验一个数 M 是否合法呢?一开始我用4个set维护当前区间(区间长度为M)内上下左右的边界进而检验(判断上边界的最小值与下边界最大值之差是否不小于 M ,左右边界同理),可这样是O(nlogn)的,总复杂度 O(nlog2n) 但常数不小,T掉了。后来思考,我们只关心当前区间内的最小值或最大值,可以用单调队列 O(n) 实现,更改一些细节后AC。

代码

/// by ztx
/// blog.csdn.net/hzoi_ztx
#include <bits/stdc++.h>
#define Rep(i,l,r) for(i=(l);i<=(r);i++)
#define rep(i,l,r) for(i=(l);i< (r);i++)
#define Rev(i,r,l) for(i=(r);i>=(l);i--)
#define rev(i,r,l) for(i=(r);i> (l);i--)
#define Each(i,v)  for(i=v.begin();i!=v.end();i++)
#define r(x)   read(x)
typedef long long ll ;
typedef double lf ;
int CH , NEG ;
template <typename TP>inline void read(TP& ret) {
    ret = NEG = 0 ; while (CH=getchar() , CH<'!') ;
    if (CH == '-') NEG = true , CH = getchar() ;
    while (ret = ret*10+CH-'0' , CH=getchar() , CH>'!') ;
    if (NEG) ret = -ret ;
}

#define  kN  100010LL

struct node {
    int val,id;
    bool operator < (const node&b)const{ if (val==b.val) return id<b.id; return val<b.val; }
};

int N, M, K, Q, P;
int lef[kN], dow[kN], rig[kN], up[kN];

bool ok(int d) {
    //std::set<node>su,sd,sl,sr;
    std::deque<int>su,sd,sl,sr;
    node tmp;
    for(int i=1,tu,td,tl,tr;i<=N;i++) {
        while (!su.empty()&&up[i]<=up[su.back()]) su.pop_back(); su.push_back(i);
        while (!sr.empty()&&rig[i]<=rig[sr.back()]) sr.pop_back(); sr.push_back(i);
        while (!sd.empty()&&dow[i]>=dow[sd.back()]) sd.pop_back(); sd.push_back(i);
        while (!sl.empty()&&lef[i]>=lef[sl.back()]) sl.pop_back(); sl.push_back(i);
        if (i<d) continue;
        if (su.front()<=i-d) su.pop_front();
        if (sd.front()<=i-d) sd.pop_front();
        if (sl.front()<=i-d) sl.pop_front();
        if (sr.front()<=i-d) sr.pop_front();
        tu = up[su.front()];
        tr = rig[sr.front()];
        td = dow[sd.front()];
        tl = lef[sl.front()];
        if (tu-td+1>=d && tr-tl+1>=d) return true;
    }
    return false;
}

ll tot, ans, now, num;

int main() {
    int i,L,R,mid,Max;
    r(N), r(M), r(K), r(P), r(Q);
    tot=0;
    Rep (i,1,N) r(lef[i]), r(dow[i]), r(rig[i]), r(up[i]),tot+=(rig[i]-lef[i]+1LL)*(up[i]-dow[i]+1LL);
    L = 1, R = N+1;
    while (R-L > 1) { // [L,R)
        mid = (L+R)/2;
        if (ok(mid)) L=mid;
        else R=mid;
    }
    ans = 0;
    Max = L;
    Rep (i,1,Max) {
        num = tot+1LL-1LL*i*i*i;
        now = 1LL*i*P+num*Q;
        if (now>ans) ans = now;
    }
    printf("%lld\n", ans);
    END: getchar(), getchar();
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值