RMQ+二分 - CF 689D Friends and Subsequences

题目:

Friends and Subsequences

题意:

给A,B两个序列,求 maxri=l(Ai)=minri=l(Bi) 的区间数量

思路:

在所有的区间中求极大/小值,第一反应就应该是二分。
当l为定值,随着x的增大, maxxl=iArri 是一个单调不减函数,同理, minxl=iArri 是一个单调不增函数,这里不给出证明,有兴趣自己想一下为什么。
有了上面的规律,很容易想到算法:用RMQ做预处理,然后枚举区间左值,二分区间右值,找到两个函数的相交部分即可,因为不是严格递增递减函数,所以相交部分有可能是一段而不仅仅是一个点,所以做两次二分,一次找到相交部分的左端点,一次找到右端点

代码:

#include <bits/stdc++.h>
using namespace std;
const int maxn = 2e5+10;
int dmin[maxn][32], dmax[maxn][32];

void RMQ_init(int A[], int B[], int len){
    for (int i = 0; i<len; ++i){
        dmin[i][0] = A[i];
        dmax[i][0] = B[i];
    }
    for (int j = 1; (1 << j) <= len; ++j)
    for (int i = 0; i + (1 << j) - 1<len; ++i){
        dmin[i][j] = min(dmin[i][j - 1], dmin[i + (1 << (j - 1))][j - 1]);
        dmax[i][j] = max(dmax[i][j - 1], dmax[i + (1 << (j - 1))][j - 1]);
    }
    return;
}


int RMQ_min(int L, int R){
    int k = 0;
    while (1 << (k + 1) <= R - L + 1) k++;
    return min(dmin[L][k], dmin[R - (1 << k) + 1][k]);
}

int RMQ_max(int L, int R){
    int k = 0;
    while (1 << (k + 1) <= R - L + 1) k++;
    return max(dmax[L][k], dmax[R - (1 << k) + 1][k]);
}

int arr1[maxn], arr2[maxn];

int main(){
    int n;
    scanf("%d",&n);
    for (int i=0;i<n;++i)
        scanf("%d",arr1+i);
    for (int i=0;i<n;++i)
        scanf("%d",arr2+i);
    RMQ_init(arr2, arr1, n);
    long long sum = 0;
    for (int i=0;i<n;++i){
        int l = i, r = n-1, res1 = -1, res2 = -1;
        if (RMQ_min(l,r)>RMQ_max(l,r)) continue;
        while (l<=r){
            int mid = (l+r) >> 1;
            int minimum = RMQ_min(i,mid), maximum = RMQ_max(i,mid);
            if (minimum == maximum)
                res1 = res1 == -1 ? mid : max(res1, mid);
            if (minimum < maximum)
                r = mid - 1;
            else
                l = mid + 1;
        }
        if (res1 == -1) continue;
        l = i, r = res1, res2 = -1;
        while (l<=r){
            int mid = (l+r) >> 1;
            int minimum = RMQ_min(i,mid), maximum = RMQ_max(i,mid);
            if (minimum == maximum)
                res2 = res2 == -1 ? mid : min(res2, mid);
            if (minimum <= maximum)
                r = mid - 1;
            else
                l = mid + 1;
        }
        sum += res1 - res2 + 1;
    }
    cout<<sum<<endl;
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值