计蒜客第六场 微软大楼设计方案(困难)

题目链接:

https://nanti.jisuanke.com/t/15773

题解:

先对点按x排序

设Xa<=Xb

当k<=40时,可以用中等难度的方法枚举Xa再枚举Xb,当Xb-Xa>k时不符合条件,并且更大的Xb也不符合条件,所以不接下去枚举Xb。因为楼层高度最高为20所以最多计算20*20次(这里更正一下,应该是20*m次m具体为多少我也不知道,但肯定不超过40,望dalao指教)

k>40时,从枚举Xa,再对Xb进行二分,找到第一个Xb-Xa>=k-40,则在Xa至Xb之间的核心部门到Xa的距离都是<=k的,因为楼层最高为20,然后继续从Xb用开始枚举Xb,一直到Xb-Xa>k为止,同样最多计算20*20次(20*m次)

PS:表达可能不太清楚,请见谅


#include<bits/stdc++.h>
using namespace std;
#define MAX_N 200005
#define inf 0x3f3f3f3f
#define LL long long
#define uLL unsigned long long
const LL mod = 1e8;
const LL INF = 1e18;
typedef pair<int, int>P;
const double eps = 1e-6;

int h[MAX_N];
P p[MAX_N];
int minsum[MAX_N][20];
void init_RMQ(int n)
{
    for(int i=1;i<=n;i++)
        minsum[i][0] = h[i];
    int k = log2(1.0*n);
    for(int j=1;j<=k;j++) {
        for(int i=1;i<=n;i++) {
            if(i+(1<<j)-1<=n) {
                minsum[i][j] = min(minsum[i][j-1], minsum[i+(1<<(j-1))][j-1]);
            }
        }
    }
}
int getMin(int i,int j)
{
    int k = (int)log2(1.0*(j-i+1));
    return min(minsum[i][k], minsum[j-(1<<k)+1][k]);
}
int main()
{
    int n, k, m;
    cin >> n >> k;
    for(int i=1; i<=n; i++) {
        scanf("%d", &h[i]);
    }
    init_RMQ(n);
    cin >> m;
    for(int i=0; i<m; i++) {
        int x, y;
        cin >> x >> y;
        p[i] = P(x, y);
    }
    sort(p, p+m);
    LL ans = 0;
    if(k<=40) {
        for(int i=0; i<m; i++) {
            for(int j=i+1; j<m; j++) {
                if(p[j].first-p[i].first>k)
                    break;
                int low = min(p[i].second, p[j].second);
                low = min(low, getMin(p[i].first, p[i].second));
                int tmp = p[i].second + p[j].second - 2*low + abs(p[i].first - p[j].first);
                if(tmp <= k)
                    ans++;
            }
        }
    }
    else {
        for(int i=0; i<m; i++) {
            int pos = lower_bound(p, p+m, P(p[i].first+k-40, 0)) - p;//p[i+1]-p[pos]之间的点到p[i]的距离都是小于等于k的
            ans += (LL)(pos-i-1);
            for(int j=pos; j<m; j++) {
                if(p[j].first-p[i].first>k)
                    break;
                int low = min(p[i].second, p[j].second);
                low = min(low, getMin(p[i].first, p[j].first));
                int tmp = p[i].second + p[j].second - 2*low + abs(p[i].first - p[j].first);
                if(tmp <= k)
                    ans++;
            }
        }
    }
    cout << ans << endl;
}



  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值