P5503 [JSOI2016]灯塔

P5503 [JSOI2016]灯塔

值域分块+ST表,也可以决策单调性来做。

发现柿子写成 \(p=\max{h_j+\sqrt{|i-j|}}-h_i\)

中间的根号不太好处理,但是,我们突然发现,这个是下标之差开根,那么是不是说明其最大是 \(\sqrt{n}\) 的呢?

于是,我们可以考虑预处理每一个 \(\sqrt{|i-j|}\) 的值对应的区间长度,然后枚举每一个值后取这个区间里的区间最大值就行了。

那么询问变成了区间最值,可以使用 ST表 维护。

时间复杂度 \(O(n\sqrt{n})\) ,好像决策单调性做法是 \(O(nlogn)/O(n)\) 的,具体见 加强版

#include<bits/stdc++.h>
using namespace std;
template <typename T>
inline void read(T &x){
	x=0;char ch=getchar();bool f=false;
	while(!isdigit(ch)){if(ch=='-'){f=true;}ch=getchar();}
	while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	x=f?-x:x;
	return ;
}

template <typename T>
inline void write(T x){
	if(x<0) putchar('-'),x=-x;
	if(x>9) write(x/10);
	putchar(x%10^48);
	return ;
}
const int K=17,N=1e5+5;
struct SegMent{int l,r;}s[N];
int st[N][K],h[N],m[N],sq[N],Log[N],n,ans,L;
double k;
inline int sqr(int x){return x*x;}
inline int ask(int l,int r){
    l=max(1,l),r=min(r,n); 
    int len=r-l+1,k=Log[len];
    return max(st[l][k],st[r-(1<<k)+1][k]);
}
int main(){
    read(n);
    k=sqrt(n);L=(int)ceil(k);
    for(int i=1;i<=n;i++) read(h[i]);
    for(int i=1;i<=L;i++){
        s[i].l=sqr(i-1)+1,s[i].r=sqr(i);
        for(int j=s[i].l;j<=s[i].r;j++) sq[j]=i;
    }
    Log[1]=0;
    for(int i=2;i<=n;i++){
        Log[i]=Log[i-1];
        if((1<<Log[i]+1)==i) Log[i]++;
    }
    for(int i=n;i>=1;i--){
        st[i][0]=h[i];
        for(int j=1;(i+(1<<j)-1)<=n;j++) st[i][j]=max(st[i][j-1],st[i+(1<<j-1)][j-1]);
    }
    for(int i=1;i<=n;i++){
    	ans=0;
        for(int j=sq[i-1];j>=1;j--) ans=max(ans,ask(i-s[j].r,i-s[j].l)+j);
        for(int j=sq[n-i];j>=1;j--) ans=max(ans,ask(i+s[j].l,i+s[j].r)+j);
        write(max(0,ans-h[i])),putchar('\n');
    }
    return 0;
}
posted @ 2021-04-22 17:12  __Anchor  阅读(34)  评论(0编辑  收藏  举报