题目大意
给出两个长度为 (n) 的序列 (a) 和 (b),计算有多少对 (l,r) 满足 (max{a_l,a_{l+1},dots,a_{r-1},a_r}=min{b_l,b_{l+1},dots,b_{r-1},b_r}).
分析
考虑 (max)((min))的一些性质,(max{a_l,a_{l+1},dots,a_{r-1},a_r}leqmax{a_l,a_{l+1},dots,a_r,a_{r+1}})((min{b_l,b_{l+1},dots,b_{r-1},b_r}geqmin{b_l,b_{l+1},dots,b_r,b_{r+1}})),那么就可以发现这个东西是单调的,所以就很容易想到去二分查找.枚举左端点,查找右端点,可以发现符合条件的右端点肯定是一段区间(可能没有),那么可以二分出这个合法区间的左端点和右端点,然后就可以直接计算贡献了,考虑到取药计算区间最值,所以可以用st表维护.
代码
#include<bits/stdc++.h>
#define REP(i,first,last) for(int i=first;i<=last;++i)
#define DOW(i,first,last) for(int i=first;i>=last;--i)
using namespace std;
const int MAXN=3e5+7;
int n;
int a[MAXN];
int log_2[MAXN];
int lrmax[MAXN][24];
int lrmin[MAXN][24];
int QueryMax(int left,int right)//查询区间最大值
{
int len=log_2[right-left+1];
return max(lrmax[left][len],lrmax[right-(1<<len)+1][len]);
}
int QueryMin(int left,int right)//查询区间最小值
{
int len=log_2[right-left+1];
return min(lrmin[left][len],lrmin[right-(1<<len)+1][len]);
}
int main()
{
scanf("%d",&n);
REP(i,1,n)
{
scanf("%d",&lrmax[i][0]);
}
REP(i,1,n)
{
scanf("%d",&lrmin[i][0]);
}
int now=0;
REP(i,1,n)//预处理log2(向下取整)
{
if(i==(1<<(now+1)))
{
++now;
}
log_2[i]=now;
}
REP(i,1,now)//预处理st表
{
REP(j,1,n+1-(1<<i))
{
lrmax[j][i]=max(lrmax[j][i-1],lrmax[j+(1<<(i-1))][i-1]);
lrmin[j][i]=min(lrmin[j][i-1],lrmin[j+(1<<(i-1))][i-1]);
}
}
int left,right,middle,l_appear,r_appear;
long long answer=0;//注意开long long
REP(i,1,n)
{
left=i;
right=n;
l_appear=0;
r_appear=0;
while(left<=right)//二分右端点
{
middle=(left+right)>>1;
if(QueryMax(i,middle)>QueryMin(i,middle))//如果max大了那么就减小
{
right=middle-1;
}
else
{
if(QueryMax(i,middle)==QueryMin(i,middle))//如果max和min相等则记录位置
{
r_appear=middle;
}
left=middle+1;
}
}
left=i;
right=n;
while(left<=right)//同理二分出左端点
{
middle=(left+right)>>1;
if(QueryMax(i,middle)>=QueryMin(i,middle))
{
if(QueryMax(i,middle)==QueryMin(i,middle))
{
l_appear=middle;
}
right=middle-1;
}
else
{
left=middle+1;
}
}
if(l_appear&&r_appear)//如果存在则计算贡献
{
answer+=(r_appear-l_appear+1);
}
}
printf("%lld",answer);
return 0;
}