【XJOI】山

题目链接(咕咕咕。。。)

题目描述:

xyz现在站在一个斜坡面前这个斜坡上依次排布这\(n\)座山峰,xyz打算爬上其中的一座因为xyz体力不好,所以他只能爬上最矮的一座山又因为xyz不擅长分类讨论,因此即使山的海拔为负,他也只打算爬海拔最低的那座,而不是海拔的绝对值最小的那座然而xyz智商拙计,只带了一张相对海拔高度地图,于是要来求助你现在他知道这个斜坡有\(m\)种可能的斜率,请你对于每种斜率输出海拔最低的山峰的高度我们定义每两座山之间的水平距离都为1,第0座山所在的土地海拔高度为0

输入格式:

第一行两个数\(n\),\(m\)
第二行有\(n\)个数,表示每座山峰的高度
接下来\(m\)行每行有一个数\(p\),表示这个斜坡可能的斜率(可能小于0)

输出格式:

对于每个询问输出一行,表示最矮的山峰的海拔高度

样例输入:

3 1
3 2 1
10

样例输出:

13

数据范围:

30%的数据 \(n,m<=1000\)
100%的数据 \(n,m<=300000,max(|h[i]|)<10^12,max(|h[i]+i*p|)<10^18\),

时间限制:

1s

空间限制:

64MB

提示:

remove!!!

题解

\(n\)座山峰,还有一个斜率,求每个山峰加上它到0的距离乘上斜率的值后的最小山峰。
因为山峰的高度是相对的,所以加上的斜率只要是相对的就行了,就相当于以斜率这条直线为标准,求最下面的那个点。
如图:斜率为正,红色为山顶到斜率的距离,蓝色为最低的山顶。
斜率为正时,与原来相比左边比右边增加的高度更少,也就是左边的山底比右边的山底相对于标准线更低,所以标准线是左边高右边低的。
标准线平移没有影响。
lVUIjiouMXHB5bE.png
我们把山只留下山顶作为二维平面上的一个点,而把斜率看成是旋转平面的操作,
手膜以下旋转的过程,每次的\(ans\)都是最下面的那个点,把这些点连起来貌似就是一个下凸壳,
那么只要斜率在一个点和它左边的点的斜率以及这个点和它右边的点的斜率的中间,那么这个点就是\(ans\)了。
那么我们就可以用二分,用\(\log_{2}n\)的时间求出\(ans\)就行了。
预处理凸包时间复杂度为\(O(n)\),询问的时间复杂度为\(m\log_{2}n\),不会TLE。
注意:山的高度在\(10^18\)以内,所以要用\(long long\)
上代码:

#include<bits/stdc++.h>
#define inf 1e18+1
using namespace std;
int n,m;
long long a[300009];
long long mn,xmn;
long long p;
struct aa{
    long long h,x;
}pp[300009];
int lp=2;
bool pd(int x,int l){
    if(l<2) return 0;
    return 1.0*(a[x]-pp[lp].h)/(x-pp[lp].x)<1.0*(pp[lp].h-pp[lp-1].h)/(pp[lp].x-pp[lp-1].x);
}
double xl(int x1,long long h1,int x2,long long h2){
    if(x1==x2){
        if(h1>h2) return inf;
        return -inf;
    }
    return 1.0*(h1-h2)/(x1-x2);
}
int main(){
    scanf("%d%d",&n,&m);
    for(int j=1;j<=n;j++)
        scanf("%lld",&a[j]);
    pp[1].h=a[1];pp[2].h=a[2];
    pp[1].x=1;pp[2].x=2;
    for(int j=3;j<=n;j++){
        while(pd(j,lp)) lp--;
        pp[++lp].x=j;
        pp[lp].h=a[j];
    }
    pp[0]=pp[1];
    pp[lp+1]=pp[lp];
    pp[0].h=pp[lp+1].h=inf;
    while(m--){
        scanf("%lld",&p);
        p=-p;
        int l=1,r=lp,ans;
        while(l<=r){
            int mid=(l+r)/2;
            if(p>=xl(pp[mid].x,pp[mid].h,pp[mid-1].x,pp[mid-1].h) && p<=xl(pp[mid+1].x,pp[mid+1].h,pp[mid].x,pp[mid].h)){
                ans=mid;
                break;
            }else if(p<xl(pp[mid].x,pp[mid].h,pp[mid-1].x,pp[mid-1].h)) r=mid-1;
            else l=mid+1;
        }
        printf("%lld\n",pp[ans].h-pp[ans].x*p);
    }
    return 0;
}

转载于:https://www.cnblogs.com/linjiale/p/11303049.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值