Codeforces 765F Souvenirs 线段树

题意

有一个长度为 n n 的序列a[1..n],每次询问区间 [l,r] [ l , r ] 中任意两个数的差的绝对值的最小值。
n105,m3105 n ≤ 10 5 , m ≤ 3 ∗ 10 5

分析

从小到大枚举右端点,用线段树维护每个点作为左端点时的答案。
考虑每次新加入一个点 a[i] a [ i ] 时要如何维护答案。
这里只考虑维护所有 a[j]a[j] a [ j ] ≤ a [ j ] j<i j < i 时的答案,另一种情况可以把 a a 数组取反后再做一遍。
我们可以先找到i前面第一个不大于 a[i] a [ i ] 的数 a[j] a [ j ] ,然后用 a[i]a[j] a [ i ] − a [ j ] 来 更 新 ans[1..j] a n s [ 1.. j ] 。然后再找到 j j 前面第一个大于a[j]且不大于 a[i] a [ i ] 的数 a[k] a [ k ] ,然后用 a[i]a[k] a [ i ] − a [ k ] 来更新 ans[1..k] a n s [ 1.. k ] 。如此类推。
这样做是 O(n2logn) O ( n 2 l o g n ) 的,但这里有一个优化,就是 a[k] a [ k ] 必须要满足 a[k]a[j]>a[i]a[k] a [ k ] − a [ j ] > a [ i ] − a [ k ]
为什么呢?因为如果不满足的话,那么 ans[1..k] a n s [ 1.. k ] 一定不会取 a[i]a[k] a [ i ] − a [ k ] 作为答案,而是取 a[k]a[j] a [ k ] − a [ j ] 作为答案。
这样的话每次取的点到 a[i] a [ i ] 的距离就会至少减少一半,就可以过了。

代码

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<vector>

const int N=100005;
const int inf=1000000000;

int n,m,ans[N*3],a[N],lef[N*3];
struct tree{int mn;}t[N*4];
std::vector<int> vec[N];

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;
}

struct Segment_Tree
{
    int rt[N],sz;
    struct tree{int l,r,mx;}t[N*33];

    void ins(int &d,int l,int r,int x,int y)
    {
        int p=d;d=++sz;t[d]=t[p];t[d].mx=std::max(t[d].mx,y);
        if (l==r) return;
        int mid=(l+r)/2;
        if (x<=mid) ins(t[d].l,l,mid,x,y);
        else ins(t[d].r,mid+1,r,x,y);
    }

    int query(int d,int l,int r,int x,int y)
    {
        if (!d) return 0;
        if (x<=l&&r<=y) return t[d].mx;
        int mid=(l+r)/2,ans=0;
        if (x<=mid) ans=std::max(ans,query(t[d].l,l,mid,x,y));
        if (y>mid) ans=std::max(ans,query(t[d].r,mid+1,r,x,y));
        return ans;
    }
}seg;

void build(int d,int l,int r)
{
    t[d].mn=inf;
    if (l==r) return;
    int mid=(l+r)/2;
    build(d*2,l,mid);build(d*2+1,mid+1,r);
}

void modify(int d,int l,int r,int x,int y,int z)
{
    if (x<=l&&r<=y) {t[d].mn=std::min(t[d].mn,z);return;}
    int mid=(l+r)/2;
    if (x<=mid) modify(d*2,l,mid,x,y,z);
    if (y>mid) modify(d*2+1,mid+1,r,x,y,z);
}

int find(int d,int l,int r,int x)
{
    if (l==r) return t[d].mn;
    int mid=(l+r)/2;
    if (x<=mid) return std::min(t[d].mn,find(d*2,l,mid,x));
    else return std::min(t[d].mn,find(d*2+1,mid+1,r,x));
}

void solve()
{
    build(1,1,n);
    for (int i=1;i<=n;i++)
    {
        int x=a[i],p=seg.query(seg.rt[i-1],0,inf,0,x);
        while (p)
        {
            modify(1,1,n,1,p,x-a[p]);
            p=seg.query(seg.rt[p-1],0,inf,(x+a[p])/2+1,x);
        }
        for (int j=0;j<vec[i].size();j++) ans[vec[i][j]]=std::min(ans[vec[i][j]],find(1,1,n,lef[vec[i][j]]));
    }
}

int main()
{
    n=read();
    for (int i=1;i<=n;i++) a[i]=read();
    for (int i=1;i<=n;i++) seg.rt[i]=seg.rt[i-1],seg.ins(seg.rt[i],0,inf,a[i],i);
    m=read();
    for (int i=1;i<=m;i++)
    {
        int l=read(),r=read();
        vec[r].push_back(i);
        lef[i]=l;ans[i]=inf;
    }
    solve();
    for (int i=1;i<=n;i++) a[i]=inf-a[i];
    seg.sz=0;
    for (int i=1;i<=n;i++) seg.rt[i]=seg.rt[i-1],seg.ins(seg.rt[i],0,inf,a[i],i);
    solve();
    for (int i=1;i<=m;i++) printf("%d\n",ans[i]);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值