[CF765F] Souvenirs

Portal

其实这题很套路。像HNOI2017影魔的模型,大概可以概括为:

给定你一个序列,在不修改的前提下,求一个区间满足某种条件的二元组的个数/最值/和,条件一般和最值有关, 这就方便我们进行维护。

解决此类问题的一般思路是,首先离线,然后在固定某个端点的情况下处理另一个端点。处理容易发现,另一个端点的值往往是满足某些类单调的性质。这就可以严格限制另一个端点扫到的元素个数,从而保证复杂度。 这里一般开一个线段树进行维护。

在本题中,有端点必须满足\[ j' - j < j - i \], 从而推出j递减的个数为\(O(logn)\)次,可以采用主席树进行维护,进而用线段树维护历史二元组的值。

复杂度似乎是 \(O(n\sqrt n)\) 的莫队,链接

详细题解:

转载自:这里

将所有询问离线,按右端点排序。

枚举右端点r,用线段树维护对于当前的右端点,每个左端点对应的答案。

线段树每个结点维护对应区间归并排序后的状态,即构造一棵归并树。

插入新的右端点就相当于在线段树对应区间找到最接近的数,然后更新答案。

显然暴力更新的复杂度在最坏情况下是单次\(O(n)\)的。

发现由于右端点固定,若左边的答案已经不会比右边优,则左端点已经没有更新的必要。先更新右子区间,再更新左子区间剪枝即可。

时间复杂度\(O(mlog^2n)\)

Code:

#include<bits/stdc++.h>
using namespace std;
#define rep(i, a, b) for(int i = (a), i##_end_ = (b); i <= i##_end_; ++i)
#define drep(i, a, b) for(int i = (a), i##_end_ = (b); i >= i##_end_; --i)
#define clar(a, b) memset((a), (b), sizeof(a))
#define debug(...) fprintf(stderr, __VA_ARGS__)
typedef long long LL;
typedef long double LD;
int read() {
    char ch = getchar();
    int x = 0, flag = 1;
    for (;!isdigit(ch); ch = getchar()) if (ch == '-') flag *= -1;
    for (;isdigit(ch); ch = getchar()) x = x * 10 + ch - 48;
    return x * flag;
}
void write(int x) {
    if (x < 0) putchar('-'), x = -x;
    if (x >= 10) write(x / 10);
    putchar(x % 10 + 48);
}

const int Maxn = 2e5 + 9, Maxm = 4e5 + 9, INF = 0x3f3f3f3f;
int n, m, a[Maxn];
vector <pair<int, int> > ls[Maxn];
int ans[Maxm];

struct BIT {
    int c[Maxn];
#define lowbit(x) ((x) & -(x))
    void modify(int pos, int val) {
        for (; pos; pos -= lowbit(pos)) c[pos] = min(c[pos], val);
    }
    int query(int pos) {
        int res = 0x3f3f3f3f;
        for (; pos < Maxn; pos += lowbit(pos)) res = min(res, c[pos]);
        return res;
    }
#undef lowbit
}s;

int rt[Maxn];

struct PresidentTree {
    int ls[Maxn * 32], rs[Maxn * 32], Max[Maxn * 32], cnt;
    void init() {
        clar(ls, 0); clar(rs, 0); clar(Max, 0); cnt = 0;
    }
    int modify(int pre, int l, int r, int pos, int val) {
        int o = ++cnt;
        ls[o] = ls[pre]; rs[o] = rs[pre]; Max[o] = max(Max[o], val);
        if (l == r) return o;
        int mid = (l + r) >> 1;
        (pos <= mid) ? ls[o] = modify(ls[pre], l, mid, pos, val) : rs[o] = modify(rs[pre], mid + 1, r, pos, val);
        return o;
    }

    int query(int rt, int l, int r, int p, int q) {
        if (p > q || !rt) return 0;
        if (p <= l && r <= q) return Max[rt];
        int mid = (l + r) >> 1;
        if (q <= mid) return query(ls[rt], l, mid, p, q);
        else if (p >= mid + 1) return query(rs[rt], mid + 1, r, p, q);
        else return max(query(rs[rt], mid + 1, r, p, q), query(ls[rt], l, mid, p, q));
    }
}t;

void init() {
    clar(ans, 0x3f);

    n = read(); 
    rep (i, 1, n) a[i] = read();
    m = read();
    rep (i, 1, m) {
        int l = read(), r = read();
        ls[r].push_back(make_pair(l, i));
    }
}

void calc() {
    t.init(); clar(s.c, 0x3f);
    rep (i, 1, n) rt[i] = t.modify(rt[i - 1], 1, 0x3f3f3f3f, a[i], i);

    rep (i, 1, n) {
        int cur = t.query(rt[i - 1], 1, 0x3f3f3f3f, a[i], 0x3f3f3f3f);

        while (cur != 0) {
            s.modify(cur, a[cur] - a[i]);
            cur = t.query(rt[cur - 1], 1, 0x3f3f3f3f, a[i], (a[i] + a[cur]) / 2 - !((a[i] + a[cur]) & 1));
        }

        rep (j, 0, ls[i].size() - 1) ans[ls[i][j].second] = min(ans[ls[i][j].second], s.query(ls[i][j].first)); 
    }
}

void solve() {
    calc();

    rep (i, 1, n) a[i] = 0x3f3f3f3f - a[i];
    calc();
    
    rep (i, 1, m) printf("%d\n", ans[i]);
}

int main() {
    freopen("bridge.in", "r", stdin);
    freopen("bridge.out", "w", stdout);

    init();
    solve();

#ifdef Qrsikno
    debug("\nRunning time: %.3lf(s)\n", clock() * 1.0 / CLOCKS_PER_SEC);
#endif
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/qrsikno/p/10128898.html