1. 程式人生 > >luogu P3246 [HNOI2016]序列 莫隊+ST表

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

題意

  • 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;
}