【Codeforces Round #397】Codeforces 765F Souvenirs【解法二】

解法一(线段树)见这里
我们维护一棵线段树,树上每个结点用平衡树维护含有的元素,并且维护这个区间中的一个值和这个区间右边的另一个值的差的最小值。按区间的左端点从右往左扫描,每次用 ai 对区间 (i,n] 进行覆盖,询问的时候直接询问区间 (l,r] 最小值。
覆盖的时候一般来说需要递归到底,但是这样复杂度还不如暴力。一个重要的剪枝是维护之前已经遍历的区间的前缀最小值【因为递归的过程本来就是前缀实现的】,如果当前区间没有比这个值更优的解,就不再修改,直接返回。
不难发现线段树套平衡树也可以用主席树实现。

#include<cstdio>
#include<algorithm>
#include<set>
#include<vector>
using namespace std;
const int maxn=2000010,oo=0x3f3f3f3f;
int rd()
{
    int x=0;
    char c=getchar();
    while (c<'0'||c>'9') c=getchar();
    while (c>='0'&&c<='9')
    {
        x=x*10+c-'0';
        c=getchar();
    }
    return x;
}
set<int> s[maxn];
vector<int> qry[maxn],v[maxn];
int mn[maxn],a[maxn],l[maxn],r[maxn],ans[maxn],
n,q;
void build(int u,int L,int R)
{
    mn[u]=oo;
    for (int i=L;i<=R;i++) s[u].insert(a[i]);
    if (L==R) return;
    int mid=(L+R)>>1;
    build(u<<1,L,mid);
    build(u<<1|1,mid+1,R);
}
int query(int u,int L,int R,int l,int r)
{
    if (l<=L&&R<=r) return mn[u];
    int mid=(L+R)>>1;
    int ret=oo;
    if (l<=mid) ret=min(ret,query(u<<1,L,mid,l,r));
    if (r>mid) ret=min(ret,query(u<<1|1,mid+1,R,l,r));
    return ret;
}
void modify(int u,int L,int R,int l,int r,int x,int &res)
{
    set<int>::iterator i1=s[u].lower_bound(x),i2;
    if (i1!=s[u].begin())
    {
        i2=i1;
        i2--;
    }
    if ((i1==s[u].end()||*i1-x>=res)&&(i1==s[u].begin()||x-*i2>=res))
    {
        res=min(res,query(u,L,R,l,r));
        return;
    }
    /*if (l<=L&&R<=r)
    {
        if (i1!=s.end()) mn[u]=min(mn[u],*it-x);
        if (i1!=s.begin()) mu[u]=min(mn[u],x-*it);
        v[u].push_back(x);
    }*/
    if (L==R)
    {
        mn[u]=min(mn[u],abs(a[L]-x));
        res=min(res,mn[u]);
        return;
    }
    int mid=(L+R)>>1;
    if (l<=mid) modify(u<<1,L,mid,l,r,x,res);
    if (r>mid) modify(u<<1|1,mid+1,R,l,r,x,res);
    mn[u]=min(mn[u<<1],mn[u<<1|1]);
}
int main()
{
    int res;
    n=rd();
    for (int i=1;i<=n;i++) a[i]=rd();
    q=rd();
    for (int i=1;i<=q;i++)
    {
        l[i]=rd();
        r[i]=rd();
        qry[l[i]].push_back(i);
    }
    build(1,1,n);
    for (int i=n-1;i;i--)
    {
        res=oo;
        modify(1,1,n,i+1,n,a[i],res);
        for (vector<int>::iterator it=qry[i].begin();it!=qry[i].end();++it)
            ans[*it]=query(1,1,n,i+1,r[*it]);
    }
    for (int i=1;i<=q;i++) printf("%d\n",ans[i]);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值