LOJ 6057 - [HNOI2016]序列 加强版再加强版

Posted acha

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了LOJ 6057 - [HNOI2016]序列 加强版再加强版相关的知识,希望对你有一定的参考价值。

Description

给定一个长度为 \(n\le 3*10^6\) 的序列
\(q\le 10^7\) 次询问每次求区间 \([l,r]\) 的所有子区间的最小值的和
询问随机

Solution

考虑求出区间的最小值, 设在位置 \(p\)
考虑 \([l, p)\)\((p, r]\) 的答案
\([l, p) = [l, n] - [p, n] - (左端点在[l, p) 右端点在[p, n] 的)\)
因为 \([l, p)\) 都比 \(p\)
所以该部分为 \((p-l) * 左端点在p的答案\)

区间最小值可以用rmq求
正常的O(n)-O(1) rmq需要转成树, 然后变成 \(\pm 1\) 的, 然后分块块内还要预处理, 常数很大
注意到这题询问随机, 询问到块内的几率很小
所以块内暴力, 块外跟正常做法一样

Code

#include <bits/stdc++.h>
using namespace std;
#define ri rd<int>
#define rep(i, a, b) for (int i = (a), _ = (b); i <= _; ++i)
#define per(i, a, b) for (int i = (a), _ = (b); i >= _; --i)
#define For(i, a, b) for (int i = (a), _ = (b); i < _; ++i)
const int maxN = 3e6 + 7;
const int INF = 1e9 + 7;
typedef long long LL;
const LL O = 1e9 + 7;

template<class T> T rd() {
    bool f = 1; char c = getchar(); for (; !isdigit(c); c = getchar()) if (c == ‘-‘) f = 0;
    T x = 0; for (; isdigit(c); c = getchar()) x = x * 10 + c - ‘0‘; return f ? x : -x;
}

int n, m;
int a[maxN];

namespace IO {
    int A, B, C, P;
    LL lastAns;

    inline int rnd() {
        return A = (A * B + (C ^ (int)(lastAns & 0x7fffffffLL)) % P) % P;   
    }

    void init() {
        A = ri(), B = ri(), C = ri(), P = ri();
        lastAns = 0;
    }
}

namespace RMQ {
    const int maxL = 3e5 + 7;
    const int B = 12;
    struct Node {
        int l, r, v;
        int pre[12], suf[12];
    }a[maxL];

    int st[maxL][20];
    int ln[maxL];
    int bl[maxN];

    void gmin(int &x, int y) {
        if (::a[y] < ::a[x]) x = y;
    }

    int ggmin(int x, int y) {
        return ::a[x] < ::a[y] ? x : y;
    }

    void init() {
        rep (i, 1, n) {
            int &t = bl[i] = i / B;
            int &v = st[t][0];
            if (a[t].l == 0) {
                a[t].l = i;
                v = i;
            }
            a[t].r = i;
            gmin(v, i);
        }

        int T = bl[n];
        rep (j, 0, T) {
            Node &t = a[j];
            int l = t.l, r = t.r;
            t.pre[0] = l;
            rep (i, l+1, r) t.pre[i-l] = ggmin(t.pre[i-l-1], i);
            t.suf[0] = r;
            per (i, r-1, l) t.suf[r-i] = ggmin(t.suf[r-i-1], i);
        }

        rep (i, 2, T) ln[i] = ln[i >> 1] + 1;
        per (i, T, 0) {
            rep (j, 1, ln[T-i+1])
                st[i][j] = ggmin(st[i][j-1], st[i+(1<<(j-1))][j-1]);
        }
    }

    int eval(int l, int r) {
        int len = ln[r-l+1];
        return ggmin(st[l][len], st[r-(1<<len)+1][len]);
    }

    int get(int l, int r) {
        int res = l;
        if (bl[l] == bl[r]) {
            rep (i, l, r) gmin(res, i);
            return res;
        }
        int u = bl[l], v = bl[r];
        gmin(res, a[u].suf[a[u].r - l]);
        gmin(res, a[v].pre[r - a[v].l]);
        if (u+1 < v) gmin(res, eval(u+1, v-1));
        return res;
    }
}

namespace Solve {
    LL pre[maxN], suf[maxN];

    void init() {
        static int stack[maxN], Top;
        stack[Top = 0] = 0;
        LL res = 0;
        rep (i, 1, n) {
            for (; Top && a[stack[Top]] >= a[i]; --Top)
                res -= 1LL * a[stack[Top]] * (stack[Top] - stack[Top-1]);
            stack[++Top] = i;
            res += 1LL * a[i] * (stack[Top] - stack[Top-1]);
            pre[i] = res;
        }
        res = 0;
        per (i, n, 1) {
            for (; Top && a[stack[Top]] >= a[i]; --Top)
                res -= 1LL * a[stack[Top]] * (stack[Top-1] - stack[Top]);
            stack[++Top] = i;
            res += 1LL * a[i] * (stack[Top-1] - stack[Top]);
            suf[i] = res;
        }
        rep (i, 1, n) pre[i] += pre[i-1];
        per (i, n, 1) suf[i] += suf[i+1];
    }

    LL getr(int l, int r) {
        return suf[l] - suf[r] - 1LL * (r-l) * (suf[r] - suf[r+1]);
    }

    LL getl(int l, int r) {
        return pre[r] - pre[l] - 1LL * (r-l) * (pre[l] - pre[l-1]);
    }
}

LL get(int l, int r) {
    int p = RMQ::get(l, r); 
    LL res = 1LL * (p-l+1) * (r-p+1) * a[p];
    if (l < p) res += Solve::getr(l, p);
    if (p < r) res += Solve::getl(p, r);
    return res;
}

int main() {
#ifndef ONLINE_JUDGE
    freopen("a.in", "r", stdin);
#endif

    n = ri(), m = ri();
    rep (i, 1, n) a[i] = ri();
    IO::init();
    RMQ::init();
    Solve::init();

    LL res = 0;
    rep (i, 1, m) {
        int l = IO::rnd() % n + 1, r = IO::rnd() % n + 1;
        if (l > r) std::swap(l, r);
        LL &tp = IO::lastAns = get(l, r);
        res += tp % O;
    }
    printf("%lld\n", (res % O + O) % O);

    return 0;
}

以上是关于LOJ 6057 - [HNOI2016]序列 加强版再加强版的主要内容,如果未能解决你的问题,请参考以下文章

loj3059hnoi2019序列

loj2052 「HNOI2016」矿区

loj2048 「HNOI2016」最小公倍数

Loj 2028 随机序列

loj2509 hnoi2018排列

「HNOI2016」序列