[bzoj4850][Jsoi2016]灯塔
来自FallDream的博客,未经允许,请勿转载,谢谢。
JSOI的国境线上有N一座连续的山峰,其中第ii座的高度是hi??.为了简单起见,我们认为这N座山峰排成了连续一条
直线.如果在第ii座山峰上建立一座高度为p(p≥0)的灯塔,JYY发现,这座灯塔能够照亮第jj座山峰,当且仅当满足如
下不等式:hj≤hi+p-(是减号)sqrt(|i-j|)JSOI国王希望对于每一座山峰,JYY都能提供建造一座能够照亮全部其他山峰的灯
塔所需要的最小高度.你能帮助JYY么?
1< N ≤ 10^5
0 < hi ≤ 10^9
话说这个出题人非常的不走心 同bzoj2216 5年前的题 样例都不改就扔了一个假的公式出来。
暴力比较显然,可以预处理rmq,然后根号枚举。
考虑两个点j,k,那么显然一个点分别从两个点转移的情况都是一段区间,可以二分求出最小的满足从j转移比从k转移优的i。
然后开一个单调队列,维护队列中两两相邻元素,前一个比后一个优的最小的i 单调递增。
复杂度nlogn
#include<iostream> #include<cstdio> #include<cmath> #define getchar() (*S++) #define MN 500000 #define INF 2000000000 char B[1<<26],*S=B; using namespace std; inline int read() { int x = 0 , f = 1; char ch = getchar(); while(ch < '0' || ch > '9'){ if(ch == '-') f = -1; ch = getchar();} while(ch >= '0' && ch <= '9'){x = x * 10 + ch - '0';ch = getchar();} return x * f; } int n,a[MN+5],top,tail,q[MN+5]; double F[MN+5],G[MN+5],sq[MN+5]; inline int My_abs(int x){return x<0?-x:x;} double Get(int x,int y){return a[x]+sq[My_abs(y-x)];} int Calc(int x,int y) { int l=y,r=n,mid,ans=INF; while(l<=r) { mid=l+r>>1; if(Get(y,mid)>=Get(x,mid)) ans=mid,r=mid-1; else l=mid+1; } return ans; } void Solve(double*f) { top=0;tail=1; for(register int i=1;i<=n;++i) { if(top<tail||a[i]>a[q[top]]) { while(top>tail&&Calc(q[top],i)<=Calc(q[top-1],q[top])) --top; q[++top]=i; } while(top>tail&&Calc(q[tail],q[tail+1])<=i) ++tail; f[i]=Get(q[tail],i)-a[i]; } } int main() { fread(B,1,1<<26,stdin); n=read(); for(int i=0;i<=n;++i) sq[i]=sqrt(i); for(int i=1;i<=n;++i) a[i]=read(); Solve(F); for(int i=1;i<=n>>1;++i) swap(a[i],a[n+1-i]); Solve(G); for(int i=1;i<=n;++i) printf("%d\n",max(0,(int)ceil(max(F[i],G[n+1-i])))); return 0; }
FallDream代表秋之国向您问好!
欢迎您来我的博客www.cnblogs.com/FallDream