Codeforces Round #397 (Div. 1 + Div. 2 combined) F.Souvenir

Codeforces传送门
洛谷传送门

题目描述

Artsem is on vacation and wants to buy souvenirs for his two teammates. There are n n n souvenir shops along the street. In i − t h i -th ith shop Artsem can buy one souvenir for a i a_{i} ai dollars, and he cannot buy more than one souvenir in one shop. He doesn’t want to introduce envy in his team, so he wants to buy two souvenirs with least possible difference in price.

Artsem has visited the shopping street m m m times. For some strange reason on the i − t h i -th ith day only shops with numbers from l i l_{i} li to r i r_{i} ri were operating (weird? yes it is, but have you ever tried to come up with a reasonable legend for a range query problem?). For each visit, Artsem wants to know the minimum possible difference in prices of two different souvenirs he can buy in the opened shops.

In other words, for each Artsem’s visit you should find the minimum possible value of ∣ a s − a t ∣ |a_{s}-a_{t}| asat where l i ≤ s , t ≤ r i l_{i}\le s,t\le r_{i} lis,tri, s ≠ t s≠t s̸=t .

输入输出格式

输入格式:

The first line contains an integer n n n ( 2 ≤ n ≤ 1 0 5 2\le n\le 10^{5} 2n105 ).

The second line contains n n n space-separated integers a 1 , . . . , a n a_{1} , ..., a_{n} a1,...,an( 0 ≤ a i ≤ 1 0 9 0\le a_{i}\le 10^{9} 0ai109 ).

The third line contains the number of queries m m m ( 1 ≤ m ≤ 3 ⋅ 1 0 5 1\le m\le 3·10^{5} 1m3105).

Next m m m lines describe the queries. i − t h i -th ith of these lines contains two space-separated integers l i l_{i} li and r i r_{i} ri denoting the range of shops working on i − t h i -th ith day ( 1 ≤ l i &lt; r i ≤ n 1\le l_{i}&lt;r_{i}\le n 1li<rin ).

输出格式:

Print the answer to each query in a separate line.

输入输出样例

输入样例#1:
8
3 1 4 1 5 9 2 6
4
1 8
1 3
4 8
5 7
输出样例#1:
0
1
1
3

解题分析

考虑离线询问, 按右端点排序, 将原来元素从左到右逐个插入线段树, 进而修改所有右端点在 R R R的答案, 保证当一个询问右端点为 R R R的时候, 我们刚好插入到 R R R的位置。线段树每个节点维护区间 [ l , r ] [l,r] [l,r]元素的有序序列, 便于查询前驱后继, 再记录一个当前区间在插入到 R R R的时候的最优解。

不难发现, 右端点 r r r一定的情况下, 一个区间 [ l , r ] [l,r] [l,r]的答案随左端点 l l l的右移单调不减。 这是因为左端点更靠左的区间包含了靠右的区间, 这样我们就可以记录当前的最优解, 优先更新右区间。 如果最优解优于在当前区间能取到的最优解, 我们就没有必要再递归下去更改。

考虑如何和会达到最大的复杂度: 左边每一个点都与插入点的差小于最优解,一次更新会更新到最左边的叶节点, 我们可以这样构造:

. . . , n 8 − 1 , n 4 − 1 , n 2 − 1 , n , 1 ...,\frac{n}{8}-1, \frac{n}{4}-1, \frac{n}{2}-1,n,1 ...,8n1,4n1,2n1,n,1

. . . , 7 8 n + 1 , 3 4 n + 1 , 1 2 n + 1 , n ...,\frac{7}{8}n+1,\frac{3}{4}n+1,\frac{1}{2}n+1,n ...,87n+1,43n+1,21n+1,n

这样构造出来的序列期望每次更新长度为 l o g ( N ) log(N) log(N)个叶节点, 所以每次更新的复杂度上限为 l o g 2 ( N ) log^2(N) log2(N), 总复杂度为 O ( N l o g 2 ( N ) + M l o g ( N ) ) O(Nlog^2(N)+Mlog(N)) O(Nlog2(N)+Mlog(N))

每个区间可以不用 m u l t i s e t multiset multiset维护有序的区间元素, 在建树的时候用 v e c t o r vector vector记录叶节点的元素再一路归并上来就好了。

代码如下:

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cctype>
#include <climits>
#include <algorithm>
#include <vector>
#define R register
#define IN inline
#define W while
#define gc getchar()
#define MX 100500
#define INF 1000000005
#define ll long long
template <class T>
IN void in(T &x)
{
    x = 0; R char c = gc;
    for (; !isdigit(c); c = gc);
    for (;  isdigit(c); c = gc)
    x = (x << 1) + (x << 3) + c - 48;
}
template <class T> IN T min(T a, T b) {return a < b ? a : b;}
template <class T> IN T max(T a, T b) {return a > b ? a : b;}
template <class T> IN T abs(T a) {return a > 0 ? a : -a;}
struct Node {int ans; std::vector <int> vec;} tree[MX << 2];
struct Query {int lef, rig, id;} que[MX << 2];
IN bool operator < (const Query &x, const Query &y) {return x.rig < y.rig;}
int val[MX], ans[MX << 2], dot, q;
std::vector <int>::iterator it;
namespace SGT
{
    #define ls (now << 1)
    #define rs (now << 1 | 1)
    IN void pushup(R int now) {tree[now].ans = min(tree[ls].ans, tree[rs].ans);}
    IN void merge(std::vector <int> &vec, const std::vector <int> &l, const std::vector <int> &r)
    {
        R int lsiz = l.size(), rsiz = r.size(), lef = 0, rig = 0;
        W (lef < lsiz && rig < rsiz)
        {
            if(l[lef] < r[rig]) vec.push_back(l[lef++]);
            else vec.push_back(r[rig++]);
        }
        W (lef < lsiz) vec.push_back(l[lef++]);
        W (rig < rsiz) vec.push_back(r[rig++]);
    }
    void build(R int now, R int lef, R int rig)
    {
        tree[now].ans = INF;
        if(lef == rig) return tree[now].vec.push_back(val[lef]), void();
        int mid = lef + rig >> 1;
        build(ls, lef, mid), build(rs, mid + 1, rig);
        merge(tree[now].vec, tree[ls].vec, tree[rs].vec);
    }
    int query(R int now, R int lef, R int rig, R int lb, R int rb)
    {
        if(lef >= lb && rig <= rb) return tree[now].ans;
        int mid = lef + rig >> 1, ret = INF;
        if(mid >= lb) ret = min(ret, query(ls, lef, mid, lb, rb));
        if(mid <  rb) ret = min(ret, query(rs, mid + 1, rig, lb, rb));
        return ret;
    }
    void modify(R int now, R int lef, R int rig, R int rb, int &best, R int del)
    {
        if(lef == rig)
        {
            tree[now].ans = min(tree[now].ans, abs(val[lef] - del));
            best = min(best, tree[now].ans);
            return;
        }
        int mid = lef + rig >> 1;
        it = std::lower_bound(tree[now].vec.begin(), tree[now].vec.end(), del);
        if ((it == tree[now].vec.end() || *it >= del + best) && (it == tree[now].vec.begin() || *(it - 1) <= del - best))
        return tree[now].ans = query(now, lef, rig, lef, rb), best = min(tree[now].ans, best), void();
        if(rb > mid) modify(rs, mid + 1, rig, rb, best, del), modify(ls, lef, mid, rb, best, del);
        else modify(ls, lef, mid, rb, best, del);
        pushup(now);
    }
    #undef ls
    #undef rs
}
int main(void)
{
    in(dot);
    for (R int i = 1; i <= dot; ++i) in(val[i]);
    SGT::build(1, 1, dot); in(q);
    for (R int i = 1; i <= q; ++i) in(que[i].lef), in(que[i].rig), que[i].id = i;
    std::sort(que + 1, que + 1 + q);
    R int cur = 1, best;
    for (R int i = 1; i <= q; ++i)
    {
        W (cur < que[i].rig) best = INF, SGT::modify(1, 1, dot, cur, best, val[cur + 1]), ++cur;
        ans[que[i].id] = SGT::query(1, 1, dot, que[i].lef, que[i].rig);
    }
    for (R 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、付费专栏及课程。

余额充值