1. 程式人生 > >bzoj3956: Count (單調棧+st表)

bzoj3956: Count (單調棧+st表)

getc 解決 我們 一半 void pro main 解決問題 ast

題面鏈接

bzoj

題解

非常巧妙的一道題

類似[hnoi影魔]

每個點會給左右第一個大於它的點對產生貢獻

可以用單調棧求出

這裏有點小細節,就是處理相等的點時,最左邊的點管左邊的貢獻,最右邊的點管最右邊的貢獻

然後對於每個點,求出了一對\(x, y\)

那麽,對於詢問區間\(l,r\)

答案就是有多少個\(x,y\)在區間\(l,r\)之間, 即\(l<=x<=r\) && \(l<=y<=r\)

再加上相鄰的點對

這就可以用二維數點做

但是有沒有更優秀的做法呢?

我們設\(a[pos]\)為區間\([l,r]\)之間最大的數

那麽\(x\)\([l,pos-1]\)

之間的點對,\(y\)一定不會越過\(pos\)

那麽只要求出\([l,pos-1]\)之間有多少\(x\),就可以求出有多少點對\((x,y)\)\([l,pos-1]\)

同理另一半也可以求出

那麽,前綴和就可以解決問題了

Code

#include<bits/stdc++.h>

#define LL long long
#define RG register

using namespace std;
template<class T> inline void read(T &x) {
    x = 0; RG char c = getchar(); bool f = 0;
    while (c != '-' && (c < '0' || c > '9')) c = getchar(); if (c == '-') c = getchar(), f = 1;
    while (c >= '0' && c <= '9') x = x*10+c-48, c = getchar();
    x = f ? -x : x;
    return ;
}
template<class T> inline void write(T x) {
    if (!x) {putchar(48);return ;}
    if (x < 0) x = -x, putchar('-');
    int len = -1, z[20]; while (x > 0) z[++len] = x%10, x /= 10;
    for (RG int i = len; i >= 0; i--) putchar(z[i]+48);return ;
}
const int N = 300010;
int n, m, a[N];
int f[21][N], g[21][N];
int query(int l, int r) {
    int k = log2(r - l + 1);
    if (f[k][l] >= f[k][r - (1 << k) + 1]) return g[k][l];
    return g[k][r - (1 << k) + 1];
}
int L[N], R[N];
int suml[N], sumr[N];
int q[N];
int main() {
    int type;
    read(n), read(m), read(type);
    for (int i = 1; i <= n; i++) read(a[i]), f[0][i] = a[i], g[0][i] = i;
    for (int j = 1; j <= 20; j++)
        for (int i = 1; i + (1 << j) - 1 <= n; i++)
            if (f[j - 1][i] >= f[j - 1][i + (1 << (j - 1))]) {
                f[j][i] = f[j - 1][i];
                g[j][i] = g[j - 1][i];
            }
            else {
                f[j][i] = f[j - 1][i + (1 << (j - 1))];
                g[j][i] = g[j - 1][i + (1 << (j - 1))];
            }
    int top = 0;
    for (int i = 1; i <= n; i++) {
        while (top && a[q[top]] < a[i]) top--;
        if (top && a[i] != a[q[top]]) L[i] = q[top];
        q[++top] = i;
    }
    top = 0;
    for (int i = n; i; i--) {
        while (top && a[q[top]] < a[i]) top--;
        if (top && a[i] != a[q[top]]) R[i] = q[top];
        q[++top] = i;
    }
    
    /*for (int i = 1; i <= n; i++)
        printf("%d %d\n", L[i], R[i]);*/
    
    for (int i = 1; i <= n; i++)
        suml[L[i]]++, sumr[R[i]]++;
    for (int i = 1; i <= n; i++)
        suml[i] += suml[i - 1], sumr[i] += sumr[i - 1];
    int lastans = 0;
    while (m--) {
        int x, y, l, r;
        read(x), read(y);
        if (type) l = (x + lastans - 1) % n + 1, r = (y + lastans - 1) % n + 1;
        else l = x, r = y;
        if (l > r) swap(l, r);
        int pos = query(l, r);
        lastans = suml[pos - 1] - suml[l - 1] + sumr[r] - sumr[pos] + r - l;
        printf("%d\n", lastans);
    }
    return 0;
}

bzoj3956: Count (單調棧+st表)