题意:
给出长度都为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;
}