[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;
}
posted @ 2017-05-25 15:45  FallDream  阅读(422)  评论(0编辑  收藏  举报