codeforce 689D 【二分+RMQ】

题意:

给出长度都为n(1<=n<=2e5)的两数组a[i]与b[i],求有多少组l与r使

比如a = {1,2,3,2,1,4},b={6,7,1,2,3,2} 有两组l与r符合条件:1.l=4,r=4,max=min=2;2.l=4,r=5,max=min=2。


题解:

我们可以思考当l固定时,可以通过求解r的范围[left,righ]得到(right-left)组符合条件的l与r,这个r的范围求解可以通过二分来求解, 判断在某个范围[l,r]是否符合条件可以通过RMQ在O(1)时间内处理,具体思路如下:

当r在范围[left,right]中可能存在符合条件的值时判断mid(mid=(left+right)/2)是否符合条件,若在[l,mid]范围,Max>Min则说明符合条件的r在[eftl,mid-1]中,Max<Min则说明符合条件的r在[mid+1,right]中,当Max==Min时符合条件的r最小值在[left,mid-1]符合条件的r的最大值在[mid+1,right]中。

这样就可以在O(2*log(n))求解某个固定值l下符合条件的r的范围,再枚举l就可以得到答案,总的时间复杂度为O(n*2*log(n))


#include<iostream>
#include <stdio.h>
#include <algorithm>
#include <cmath>
#include<stdlib.h>
#include <string.h>
#include<queue>
#include<set>
#include<map>
#include<stack>
#include<time.h>
using namespace std;
#define MAX_N 200005
#define inf 0x7fffffff
#define LL long long
#define ull unsigned long long
#define mod 1000000007
LL INF=9e18;

int a[MAX_N];
int b[MAX_N];
int minsum[MAX_N][20];
int maxsum[MAX_N][20];
void init_RMQ(int n)
{
    for(int i=1;i<=n;i++)
        maxsum[i][0] = a[i], minsum[i][0] = b[i];
    int k = log2(1.0*n);
    for(int j=1;j<=k;j++) {
        for(int i=1;i<=n;i++) {
            if(i+(1<<j)-1<=n) {
                maxsum[i][j] = max(maxsum[i][j-1], maxsum[i+(1<<(j-1))][j-1]);
                minsum[i][j] = min(minsum[i][j-1], minsum[i+(1<<(j-1))][j-1]);
            }
        }
    }
}
int getMax(int i,int j)
{
    int k = (int)log2(1.0*(j-i+1));
    return max(maxsum[i][k], maxsum[j-(1<<k)+1][k]);
}
int getMin(int i,int j)
{
    int k = (int)log2(1.0*(j-i+1));
    return min(minsum[i][k], minsum[j-(1<<k)+1][k]);
}
int main()
{
    int n;
    cin >> n;
    for(int i=1;i<=n;i++)
        scanf("%d",&a[i]);
    for(int i=1;i<=n;i++)
        scanf("%d",&b[i]);
    init_RMQ(n);
    LL ans = 0;
    //固定left,二分right
    for(int i=1;i<=n;i++) {
        //如果a[i]比b[i]则跳过,因为接下来Max只会更大Min只会更小
        if(a[i] > b[i])
            continue;
        int low = i;
        int up = n;
        int l = 0;
        int r = 0;
        while(low <= up) {
            int mid = (low + up) / 2;
            int Max = getMax(i, mid);
            int Min = getMin(i, mid);
            if(Max > Min)
                up = mid - 1;
            else if(Max < Min)
                low = mid + 1;
            else//假如mid符合条件则right最小值向low靠拢
                l = mid, up = mid - 1;

        }
        if(l) {//假如存在符合条件的最小值则搜索最大值
            up = n;
            low = l;
            while(low <= up) {
                int mid = (low + up) / 2;
                int Max = getMax(i, mid);
                int Min = getMin(i, mid);
                if(Max > Min)
                    up = mid - 1;
                else if(Max < Min)
                    low = mid + 1;
                else//假如mid符合条件则right最大值向up靠拢
                    r = mid, low = mid + 1;
            }
        }
        //printf("%d %d %d\n",i,l,r);
        if(l && r) {
            ans = ans + (LL)(r - l) + 1;
        }
    }
    cout << ans << endl;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值