Codeforces Round #397 F. Souvenir线段树套set

博客:http://www.cnblogs.com/qscqesze/p/6405272.html

题意

给你n个数,查询区间(l,r)中,某两个l<=i,j<=r,且i!=r的,min(abs(a[i]-a[j]))这个值。

这算是一个时间换空间的做法。
对于每一个树上的点弄一个set。

然后离线从右到左, 每次更新的时候如果 if((p==t.end()||(p-val)>=mi)&&(p==t.begin()||(val-(–p))>=mi))
这样就不用单个点地查询了。。

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e6+7;
struct node{
    int l,r,id;
    bool operator<(const node& a)const{
        if(a.l==l&&a.r==r){
            return id<a.id;
        }
        if(a.l==l)
            return r<a.r;
        return l<a.l;
    }
}query[maxn];
struct Node{
    set<int> T;
    int mi;
}T[maxn];
int n,m,b[maxn],ans[maxn];
void build(int x,int l,int r){
    T[x].mi=(1<<30);
    for(int i=l;i<=r;i++)
        T[x].T.insert(b[i]);
    if(l==r)return;
    int mid=(l+r)/2;
    build(x<<1,l,mid);
    build(x<<1|1,mid+1,r);
}
int Query(int x,int l,int r,int L,int R){
    if(l>R||L>r)return (1<<30);
    if(L<=l&&r<=R)return T[x].mi;
    int mid=(l+r)/2;
    return min(Query(x<<1,l,mid,L,R),Query(x<<1|1,mid+1,r,L,R));
}
void upd(int x,int l,int r,int L,int R,int val,int &mi){
    if(l>R||L>r)return;
    if(l==r){
        T[x].mi=min(T[x].mi,abs(val-b[l]));
        mi=min(mi,T[x].mi);
        return;
    }
    set<int> &t = T[x].T;
    auto p = t.lower_bound(val);
    if((p==t.end()||(*p-val)>=mi)&&(p==t.begin()||(val-*(--p))>=mi)){
        mi=min(mi,Query(x,l,r,L,R));//这行删掉会T,算是一个剪支,但是怎么想的...(?
        return;
    }
    int mid=(l+r)/2;
    upd(x<<1,l,mid,L,R,val,mi);
    upd(x<<1|1,mid+1,r,L,R,val,mi);
    T[x].mi=min(T[x<<1].mi,T[x<<1|1].mi);
}
void init(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%d",&b[i]);
    }
    scanf("%d",&m);
    for(int i=1;i<=m;i++)
        scanf("%d%d",&query[i].l,&query[i].r),query[i].id=i;
}
int main(){
    init();
    sort(query+1,query+1+m);
    build(1,1,n);
    for(int i=m,l=n;i;i--){
        for(;l>=query[i].l;l--){
            int inf=(1<<30);
            upd(1,1,n,l+1,n,b[l],inf);
        }
        ans[query[i].id]=Query(1,1,n,query[i].l,query[i].r);
    }
    for(int i=1;i<=m;i++)
        cout<<ans[i]<<endl;
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值