luogu P3246 [HNOI2016]序列 莫队+ST表

版权声明:_ https://blog.csdn.net/lunch__/article/details/84885718

题意

  • q q 次询问,每次询问一个区间所有子区间的最小值之和。

这个题想到莫队还是不难,难的是怎样做到 O ( 1 ) O(1) 转移,首先我们可以用 S T ST 表预处理出区间最小值的位置,并且用单调栈找到每个数左右两边第一个比它小的数记为 L [ i ] , R [ i ] L[i],R[i] 。当我们从区间 [ l , r ] [l,r] 转移到 [ l , r + 1 ] [l,r+1] 时,增加的区间就是 [ l . . . r + 1 , r + 1 ] [l...r+1,r+1] ,我们找到区间最小值的位置 p o s pos 后,对于区间 [ l . . . p o s , r + 1 ] [l...pos,r+1] 最小值显然是 a [ p o s ] a[pos] ,然后我们再计算 [ p o s + 1... r + 1 , r + 1 ] [pos+1...r+1,r+1] 的贡献,对于 r + 1 r+1 这个位置产生的贡献就是 ( r + 1 L [ r + 1 ] + 1 ) × a [ r + 1 ] (r+1-L[r+1]+1)×a[r+1] ,这个东西可以写成一个类似前缀和的形式,并且最后一定会推到 p o s pos 这个位置上来,那么这样子就可以做到 O ( 1 ) O(1) 转移了,区间长度变短的时候就减去加的贡献就好了,复杂度 O ( n n ) O(n\sqrt n)

Codes

#include <bits/stdc++.h>

using namespace std;

const int N = 1e5 + 10;
const int M = log2(1e5) + 1;

long long ans[N], dpl[N], dpr[N], res;

int n, q, top, size, ST[N][M], lg[N];
int a[N], Sta[N], be[N], L[N], R[N];

struct Que {
    int l, r, id; 
    bool operator < (const Que &T) const {
        if (be[l] == be[T.l]) return r < T.r;
        return be[l] < be[T.l];
    }
}Q[N];

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

int qmin(int x, int y) {
    int len = lg[y - x + 1];
    return _min(ST[x][len], ST[y - (1 << len) + 1][len]);
}

void insl(int l, int r) {
    int pos = qmin(l, r);
    res += 1ll * a[pos] * (r - pos + 1);
    res += dpr[l] - dpr[pos];
}

void insr(int l, int r) {
    int pos = qmin(l, r);
    res += 1ll * a[pos] * (pos - l + 1);
    res += dpl[r] - dpl[pos];
}

void dell(int l, int r) {
    int pos = qmin(l, r);
    res -= 1ll * a[pos] * (r - pos + 1);
    res -= dpr[l] - dpr[pos];
}

void delr(int l, int r) {
    int pos = qmin(l, r);
    res -= 1ll * a[pos] * (pos - l + 1);
    res -= dpl[r] - dpl[pos];
}

int main() {
#ifdef ylsakioi
    freopen("3246.in", "r", stdin);
    freopen("3246.out", "w", stdout);
#endif

    scanf("%d%d", &n, &q), size = sqrt(n);
    for (int i = 1; i <= n; ++ i)
        scanf("%d", &a[i]), ST[i][0] = i;
    for (int i = 1; i <= q; ++ i) {
        scanf("%d%d", &Q[i].l, &Q[i].r);
        Q[i].id = i, be[Q[i].l] = Q[i].l / size;
    }
    sort(Q + 1, Q + q + 1);

    for (int i = 2; i <= n; ++ i)
        lg[i] = lg[i >> 1] + 1; 
    for (int j = 1; j < M; ++ j)
        for (int i = 1; i + (1 << j) - 1 <= n; ++ i)
            ST[i][j] = _min(ST[i][j - 1], ST[i + (1 << (j - 1))][j - 1]);

    for (int i = 1; i <= n; ++ i) {
        while (top && a[Sta[top]] > a[i]) R[Sta[top --]] = i; 
        L[i] = Sta[top], Sta[++ top] = i;
    }
    while (top) R[Sta[top --]] = n + 1; 
    for (int i = 1; i <= n; ++ i) 
        dpl[i] = dpl[L[i]] + 1ll * (i - L[i]) * a[i];
    for (int i = n; i >= 1; -- i) 
        dpr[i] = dpr[R[i]] + 1ll * (R[i] - i) * a[i];

    int l = 1, r = 0;
    for (int i = 1; i <= q; ++ i) {
        while (r < Q[i].r) insr(l, ++ r);
        while (r > Q[i].r) delr(l, r --);
        while (l > Q[i].l) insl(-- l, r);
        while (l < Q[i].l) dell(l ++ , r);
        ans[Q[i].id] = res; 
    }

    for (int i = 1; i <= q; ++ i)
        printf("%lld\n", ans[i]);

    return 0;
}

猜你喜欢

转载自blog.csdn.net/lunch__/article/details/84885718