Friends and Subsequences

传送门
很棒的题。
有两个序列a和b,长度都为n,对于a序列需要知道区间的最大值,对于b序列需要知道区间的最小值
求出符合式子
(max_{i = l}^ra_i = min_{i = l}^rb_i)
的所有区间个数。

方法很简单,用st表分别维护a序列的最大值的b序列的最小值,明显,a序列的st表是一个递增函数,b序列的st表是一个递减函数
令a序列的st表减去b序列的st表,那么这个值也是递增的,如果说,值是0,那么久满足题目的情况。
那么遍历左区间,进行二分,用st表(O(1))查询找到0最后一次出现的位置和第一次出现的位置,那么答案就是[左区间, 右区间2 - 右区间1 + 1],都是满足情况的
时间复杂度(O(nlogn))

#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#define ll long long
using namespace std;
const int N = 2e5 + 5;
int a[N], b[N], n, k;
int MAX[N][20], MIN[N][20];
void init(){
    memset(MIN, 0x3f, sizeof(MIN));
    memset(MAX, 0, sizeof(MAX));
    for(int i = 1; i <= n; i++){
        MAX[i][0] = a[i];
        MIN[i][0] = b[i];
    }

    for(int j = 1; j <= 20; j++)
        for(int i = 1; i + (1 << j) - 1 <= n; i++){
            MIN[i][j] = min(MIN[i][j - 1], MIN[i + (1 << (j - 1))][j - 1]);
            MAX[i][j] = max(MAX[i][j - 1], MAX[i + (1 << (j - 1))][j - 1]);
        }
}
int Query(int l, int r){
    int k = log2(r - l + 1);
    return max(MAX[l][k], MAX[r - (1 << k) + 1][k]) - min(MIN[l][k], MIN[r - (1 << k) + 1][k]);
}
void solve(){
    scanf("%d", &n);
    for(int i = 1; i <= n; i++)
        scanf("%d", &a[i]);
    for(int i = 1; i <= n; i++)
        scanf("%d", &b[i]);
    ll ans = 0;
    init();
    for(int i = 1; i <= n; i++){
        int l = i, r = n, mid;
        while(l <= r){
            mid = (l + r) >> 1;
            int now = Query(i, mid);
            if(now > 0) r = mid - 1;
            else l = mid + 1;
        }
        ans += r - i + 1;//[i, r] 都是 ≤ 0的
        l = i, r = n;
        while(l <= r){
            mid = (l + r) >> 1;
            int now = Query(i, mid);
            if(now < 0) l = mid + 1;
            else r = mid - 1;
        }
        ans -= r - i + 1;//[i, r] 都是 ≥ 0的
    }
    printf("%lld
", ans);
}
int main(){
    solve();
    return 0;
}
原文地址:https://www.cnblogs.com/Emcikem/p/13199252.html