题目链接(咕咕咕。。。)
山
题目描述:
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的距离乘上斜率的值后的最小山峰。
因为山峰的高度是相对的,所以加上的斜率只要是相对的就行了,就相当于以斜率这条直线为标准,求最下面的那个点。
如图:斜率为正,红色为山顶到斜率的距离,蓝色为最低的山顶。
斜率为正时,与原来相比左边比右边增加的高度更少,也就是左边的山底比右边的山底相对于标准线更低,所以标准线是左边高右边低的。
标准线平移没有影响。
我们把山只留下山顶作为二维平面上的一个点,而把斜率看成是旋转平面的操作,
手膜以下旋转的过程,每次的\(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;
}