CF765F Souvenirs

题目链接

Solution

首先将询问离线,按照右端点从小到大排序。假设我们已经知道了 \([l,r-1]\) 的答案,现在将 \(a_r\) 加入更新答案。显然只需要考虑 \(a_r\) 对答案的贡献。同时建立线段树,\(seg_i\) 维护 \(i\) 位置到 \(r\) 位置的答案。

\(a_r\) 贡献答案只有 \(2\) 种情况:前面存在一个数 \(a_j > a_r\),则用 \(a_j - a_r\)\(seg_{1-j}\) 进行更新;前面存在一个数 \(a_j < a_r\),则用 \(a_r - a_j\)\(seg_{1-j}\) 进行更新。

因为这两种情况本质相同,所以我们只讲解其中的第一种情况。考虑用一种最暴力的办法:对于每个 \(r\),找到其前面第一个大于它的数,更新答案;继续找第二个...很显然这样做会 T 到飞起。那么这种暴力可以优化吗?答案是可以的,使用主席树维护权值区间的最大位置。每次使用主席树查找上一个比它大的数。

但是这样还不够,需要优化。如果 \(a_x>a_r,a_z>a_r(z<x<r)\),那么如果 \(a_x-a_z<a_z-a_r\)\(a_z-a_r\) 将不会对答案产生任何贡献。

所以我们就找到了权值的一个边界 \(\frac{a_r+a_x}{2}\)。这样每次除下去,复杂度会变成 \(\log\)

Code

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define int long long
using namespace std;

const int N = 333333, INF = 1e18;
struct que { int l, r, id; } q[N];
int n, m, pos, len, now = 1, a[N], b[N];
int minn[N << 5], tag[N << 5], hav[N << 5], maxx[N << 5], L[N << 5], R[N << 5], t[N], ans[N], cnt = 0, last = 0;

struct SegmentTree
{
    void push_up(int x) { minn[x] = min(min(minn[x], minn[x * 2]), minn[x * 2 + 1]); }
    void push_down(int x)
    {
        tag[x * 2] = min(tag[x * 2], tag[x]);
        tag[x * 2 + 1] = min(tag[x * 2 + 1], tag[x]);
        minn[x] = min(minn[x], tag[x]);
        minn[x * 2] = min(minn[x * 2], tag[x * 2]);
        minn[x * 2 + 1] = min(minn[x * 2 + 1], tag[x * 2 + 1]);
    }
    void update(int x, int l, int r, int stdl, int stdr, int k)
    {
        if(l > stdr || r < stdl) return ;
        if(stdl <= l && stdr >= r)
        {
            tag[x] = min(tag[x], k);
            minn[x] = min(minn[x], tag[x]);
            push_down(x);
            return ;
        }
        int mid = (l + r) >> 1;
        push_down(x);
        update(x * 2, l, mid, stdl, stdr, k);
        update(x * 2 + 1, mid + 1, r, stdl, stdr, k);
        push_up(x);
    }
    int query(int x, int l, int r, int stdl, int stdr)
    {
        if(l > stdr || r < stdl) return INF;
        if(stdl <= l && stdr >= r) return minn[x];
        int mid = (l + r) >> 1;
        push_down(x);
        return min(query(x * 2, l, mid, stdl, stdr), query(x * 2 + 1, mid + 1, r, stdl, stdr));
        push_up(x);
    }
} Tree1;

struct ChairmanTree
{
    int build(int l, int r)
    {
        int root = ++cnt;
        if(l < r)
        {
            int mid = (l + r) >> 1;
            L[root] = build(l, mid);
            R[root] = build(mid + 1, r);
        }
        return root;
    }
    int update(int pre, int l, int r, int x, int k)
    {
        int root = ++cnt;
        L[root] = L[pre], R[root] = R[pre];
        maxx[root] = max(maxx[pre], k);
        if(l < r)
        {
            int mid = (l + r) >> 1;
            if(x <= mid) L[root] = update(L[pre], l, mid, x, k);
            else R[root] = update(R[pre], mid + 1, r, x, k);
        }
        return root;
    }
    int query(int x, int l, int r, int stdl, int stdr)
    {
        if(b[l] > stdr || b[r] < stdl) return 0;
        if(stdl <= b[l] && stdr >= b[r]) return maxx[x];
        int mid = (l + r) >> 1;
        return max(query(L[x], l, mid, stdl, stdr), query(R[x], mid + 1, r, stdl, stdr));
    }
} Tree2;

bool cmp(que a, que b) { return a.r < b.r; }

signed main()
{
    scanf("%lld", &n);
    memset(hav, 0, sizeof(hav));
    memset(maxx, 0, sizeof(maxx));
    for(int i = 0; i < N << 5; i++) tag[i] = minn[i] = 1e18;
    for(int i = 1; i <= n; i++) scanf("%lld", &a[i]), b[i] = a[i];
    scanf("%lld", &m);
    for(int i = 1; i <= m; i++) scanf("%lld%lld", &q[i].l, &q[i].r), q[i].id = i;
    sort(q + 1, q + m + 1, cmp);
    sort(b + 1, b + n + 1);
    t[0] = Tree2.build(1, n);
    b[0] = 0;
    for(int i = 1; i <= n; i++) a[i] = lower_bound(b + 1, b + n + 1, a[i]) - b;
    for(int i = 1; i <= n; i++)
    {
        pos = 0;
        pos = Tree2.query(t[i - 1], 1, n, 0, b[a[i]]);
        if(pos != 0) Tree1.update(1, 1, n, 1, pos, b[a[i]] - b[a[pos]]);
        while(pos)
        {
            pos = Tree2.query(t[pos - 1], 1, n, (b[a[pos]] + b[a[i]]) / 2 + 1, b[a[i]]);
            if(pos <= 0) break;
            Tree1.update(1, 1, n, 1, pos, b[a[i]] - b[a[pos]]);
        }
        pos = 0;
        pos = Tree2.query(t[i - 1], 1, n, b[a[i]], b[n]);
        if(pos != 0) Tree1.update(1, 1, n, 1, pos, b[a[pos]] - b[a[i]]);
        while(pos)
        {
            if((b[a[pos]] + b[a[i]]) % 2 == 0) len = (b[a[pos]] + b[a[i]]) / 2 - 1;
            else len = (b[a[pos]] + b[a[i]]) / 2;
            pos = Tree2.query(t[pos - 1], 1, n, b[a[i]], len);
            if(pos <= 0) break;
            Tree1.update(1, 1, n, 1, pos, b[a[pos]] - b[a[i]]);
        }
        while(q[now].r <= i && now <= m)
        {
            if(q[now].r == i) ans[q[now].id] = Tree1.query(1, 1, n, q[now].l, q[now].r);
            now++;
        }
        t[i] = Tree2.update(t[i - 1], 1, n, a[i], i);
    }
    for(int i = 1; i <= m; i++) printf("%lld\n", ans[i]);
    return 0;
}
posted @ 2020-10-03 08:59  Nyxia  阅读(95)  评论(0编辑  收藏  举报