codeforces基础题——#361(div2)D

#361(Div 2) D

题目大意 :给你a, b两个长度为n的整数序列(1 <= n <= 200000), 问有多少个(l, r)(1  <= l, r  <=  n )使得a中区间(l, r)的最大值与b中区间(l, r)的最小值一样


题解 : 一开始在想神奇的树状数组瞎搞做法, 其实可以做, 但写起来很麻烦, 于是写到一半太累(懒)了, 就没再写……
此题正解十分简洁, 枚举左端点l,然后找右端点r的可行位置, 我们发现如果从i 到 i + j可行, 而i + j + 1不可行, 那么在之后应定不会有可行的右端点, 因为a中区间最大值只会增加, 而b中区间的最小值只会减少, 所以右端点r的可行位置一定在一段连续的区间中。那么怎找到这段区间呢, 注意左端点确定时,最大值与最小值具有单调性, 所以如果我们利用RMQ欲处理出最大值和最小值, 就能二分把区间找到,具体如下:
    如果a中(l, mid)大于b中(l, mid)的值, 那么(mid + 1, r)的值一定是不合法的, 所以查找区间(l, mid), 合法的值一定在(mid + 1, r)中, 查找(mid +1, r);
    通过比较大于和大于等于找出区间的左端点和右端点, 更新答案(然而我这里之前没想通,  wa了几百次)
    所以RMQ预处理, 暴力枚举l, 二分r可行区间即可, 时间复杂度O(nlogn)
#include 
#include 
#include 
using namespace std;
int a[200100], b[200100], n;
int st1[200100][21], st2[200100][21], p[21], lg[200100];
int rmq1(int i, int j)
{
    int k = lg[j - i + 1];
    return max(st1[i][k], st1[j - p[k] + 1][k]);
}
int rmq2(int i, int j)
{
    int k = lg[j - i + 1];
    return min(st2[i][k], st2[j - p[k] + 1][k]);
}
int main()
{
    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]);
    int tmp = 2;p[0] = 1;
    for (int i = 1; i <= 20; i ++) p[i] = p[i - 1] * 2;
    for (int i = 2; i <= n; i ++) {
        lg[i] = lg[i - 1];
        if (tmp <= i) lg[i] ++, tmp *= 2;
        //printf("%d %d\n", i, lg[i]);
    }
    for (int i = 1; i <= n; i ++) st1[i][0] = a[i], st2[i][0] = b[i];
    for (int i = 1; i <= 20 && p[i] <= n; i ++)
        for (int j = 1; j <= n && j + p[i] - 1 <= n; j ++){
            st1[j][i] = max(st1[j][i - 1], st1[j + p[i - 1]][i - 1]);
            st2[j][i] = min(st2[j][i - 1], st2[j + p[i - 1]][i - 1]);
        }

    long long ans = 0;
    for (int i = 1; i <= n; i ++){
        if (a[i] > b[i]) continue;
        int l = i, r = n;
        while(l != r){

            int mid = (l + r) >> 1;
            if (rmq1(i, mid) >= rmq2(i, mid)) r = mid;
            else l = mid + 1;
        }
        if (rmq1(i, r) != rmq2(i, l)) continue;
        int p = l;
        l = i, r = n + 1;
        while(l != r){
            int mid = (l + r) >> 1;
            if (rmq1(i, mid) > rmq2(i, mid)) r = mid;
            else l = mid + 1;//printf("%d %d\n", l, r);
        }
        ans += l - p;
//  
    }
    printf("%I64d\n", ans);

    return 0;
}

你可能感兴趣的:(codeforces基础题——#361(div2)D)