Codeforces Round #397 F Souvenirs(线段树)

考虑离线,我们从左往右扫,对于现在的点i,我们先找到离i最近的j使得a[j]>=a[i],然后我们可以知道询问左区间在1~j的询问的答案小于等于a[j]-a[i],我们可以用线段树更新答案,然后还可能存在f<j使得a[f]>=a[i],a[f]-a[i]<=a[j]-a[f],因为a[j]-a[f]已经更新进线段树,所以a[f]<=(a[j]+a[i)/2,我们只要找到里j最近的f即可,然后一直做先去,因为我们发先每次都是除2,所以只要做log(a[i])步就行,然后找的过程也只要离散化a[i]然后开一个查询位置的最大值的线段树即可。

#include<cstdio>
#include<cstring>
#include<iostream>
#include<cmath>
#include<algorithm>
#include<vector>
using namespace std;
const int maxn=100005;
struct pi{
    int le,ri;
    int ma;
}pp[2][maxn<<2];
void build(int u,int tot,int l,int r){
    pp[u][tot].le=l;
    pp[u][tot].ri=r;
    if(u==1) pp[u][tot].ma=1000000000;
    else pp[u][tot].ma=0;
    if(l==r) return;
    build(u,2*tot,l,(l+r)/2);
    build(u,2*tot+1,(l+r)/2+1,r);
}
void merg(int tot,int x,int p){
    pp[0][tot].ma=max(pp[0][tot].ma,p);
    if(pp[0][tot].le==pp[0][tot].ri) return;
    int mid=(pp[0][tot].le+pp[0][tot].ri)/2;
    if(x<=mid) merg(2*tot,x,p);
    else merg(2*tot+1,x,p);
}
void merg1(int tot,int l,int r,int p){
    if(pp[1][tot].le>=l&&pp[1][tot].ri<=r){
        pp[1][tot].ma=min(pp[1][tot].ma,p);
        return;
    }
    int mid=(pp[1][tot].le+pp[1][tot].ri)/2;
    if(l<=mid) merg1(2*tot,l,r,p);
    if(r>mid) merg1(2*tot+1,l,r,p);
}
int query(int tot,int l,int r){
    if(pp[0][tot].le>=l&&pp[0][tot].ri<=r) return pp[0][tot].ma;
    int s=0;
    int mid=(pp[0][tot].le+pp[0][tot].ri)/2;
    if(l<=mid) s=max(s,query(2*tot,l,r));
    if(r>mid) s=max(s,query(2*tot+1,l,r));
    return s;
}
int query1(int tot,int x){
    if(pp[1][tot].le==pp[1][tot].ri) return pp[1][tot].ma;
    int s=pp[1][tot].ma;
    int mid=(pp[1][tot].le+pp[1][tot].ri)/2;
    if(x<=mid) s=min(s,query1(2*tot,x));
    else s=min(s,query1(2*tot+1,x));
    return s;
}
struct in{
    int l,r;
    int id;
}re[maxn*3];
int a[maxn];
int b[maxn];
int ans[maxn*3];
vector<in>g[maxn];
void solve(int n,int m){
    for(int i=0;i<2;i++) build(i,1,1,n);
    for(int i=1;i<=n;i++) g[i].clear();
    for(int i=0;i<m;i++){
        g[re[i].r].push_back(re[i]);
    }
    for(int i=1;i<=n;i++){
        if(i!=1){
            int w=query(1,a[i],n);
            if(w!=0){
                merg1(1,1,w,b[a[w]]-b[a[i]]);
                int r=a[w];
                while(1){
                    int no=(b[a[i]]+b[r])/2;
                    int w=lower_bound(b+1,b+1+n,no)-b;
                    if(b[w]!=no) w--;
                    if(w<1) break;
                    if(w==r||w<a[i]) break;
                    int nn=query(1,a[i],w);
                    if(nn==0) break;
                    merg1(1,1,nn,b[a[nn]]-b[a[i]]);
                    r=a[nn];
                }
            }
        }
        merg(1,a[i],i);
        for(in v:g[i]){
            ans[v.id]=min(ans[v.id],query1(1,v.l));
        }
    }
}
int main()
{
    int n;
    cin>>n;
    for(int i=1;i<=n;i++) {
        scanf("%d",&a[i]);
        b[i]=a[i];
    }
    sort(b+1,b+1+n);
    for(int i=1;i<=n;i++) a[i]=lower_bound(b+1,b+1+n,a[i])-b;
    int m;
    scanf("%d",&m);
    for(int i=0;i<m;i++){
        scanf("%d%d",&re[i].l,&re[i].r);
        re[i].id=i;
        ans[i]=1000000000;
    }
    solve(n,m);
    for(int i=0;i<m;i++){
        int w=re[i].l;
        re[i].l=n-re[i].r+1;
        re[i].r=n-w+1;
    }
    for(int i=1;i<=n/2;i++){
        swap(a[i],a[n-i+1]);
    }
    solve(n,m);
    for(int i=0;i<m;i++) printf("%d\n",ans[i]);
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值