二分+思维好题
Description
有两个数列a,b,求有多少个区间[l,r]使得 maxri=lai=minri=lbi
Input
第一行为一个整数 n.
第二行有 n 个整数,表示 a[1]……a[n].
第三行有 n 个整数,表示 b[1]……b[n].
Output
输出一个整数,表示满足条件的区间数
Sample Input
6
1 2 3 2 1 4
6 7 1 2 3 2
Sample Output
2
Sample Explaination
有两个区间满足条件,[4,4],[4,5]
Hint
我们分析,当区间长度l增大的时候,我们可选择的数更多,所以 max(ai) 可能更大, min(ai) 可能更小,也就是说对于任何左端点x, max(ai) 和 min(bi) 满足单调性
我们更加深入的分析,即可发现对任意一个左端点x,有三种情况:
情况一
这种情况下一定存在L值满足条件,所以ans++
但仔细分析,两线一定只有一个交点吗?
不一定,因为L为一段区间的时候可能两值都相等,如图
这时我们就可以用二分处理
找出最小满足
Lleft
的和最大满足的
Lright
,然后ans+=
Lright
-
Lleft
+1
情况二
在这种情况下两值任具有单调性,具有相交的趋势,但是它们在maxlen之前没有相交,这种情况我们只能同情况一处理,即二分L,然后发现并求不出L的范围于是ans不改变
情况一和情况二都满足条件a[x]<b[x],在这种情况下我们可能可以通过扩展L使 max(ai) 变大而 min(bi) 变小从而期望在某一点达到相等
情况三
这种情况即a[x]>b[x],此时我们发现,扩展L只会使
max(ai)
变大
min(bi)
变小,两值会差的越来越多不可能相交,所以直接跳过就好了
Code
贴机房某大佬的AC代码…
#include<cstdio>
#include<cmath>
#include<algorithm>
#define redir(name) freopen(name".in","r",stdin),freopen(name".out","w",stdout)
using namespace std;
inline char gc(){
static char buf[1<<17],*p1=buf,*p2=buf;
return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<17,stdin),p1==p2)?EOF:*p1++;
}
template<class T>
inline void read(T&n){
register char ch=gc();
int sign=1;
for(n=0;(ch<'0'||ch>'9')&&ch!='-';ch=gc());
for(ch=='-'?ch=gc(),sign=-1:0;ch>='0'&&ch<='9';ch=gc()) n=(n<<1)+(n<<3)+ch-'0';
n*=sign;
}
const int N=200010;
int n;
int f[N][21],g[N][21];
inline void init(){
read(n);
for(register int i=1;i<=n;++i) read(f[i][0]);
for(register int i=1;i<=n;++i) read(g[i][0]);
f[++n][0]=2e9,g[n][0]=-2e9;
for(register int i=1;i<21;++i)
for(register int j=1;j+(1<<i)-1<=n;++j)
f[j][i]=max(f[j][i-1],f[j+(1<<(i-1))][i-1]),
g[j][i]=min(g[j][i-1],g[j+(1<<(i-1))][i-1]);
}
inline int query1(int l,int r){
const int k=log2(r-l+1);
return max(f[l][k],f[r-(1<<k)+1][k]);
}
inline int query2(int l,int r){
const int k=log2(r-l+1);
return min(g[l][k],g[r-(1<<k)+1][k]);
}
int main(){
redir("seq");
init();
register long long ans=0;
for(register int i=1;i<=n;++i){
if(f[i][0]>g[i][0]) continue;
register int l=i,r=n,mid,L=-1;
while(l<r){
mid=(l+r)>>1;
if(query1(i,mid)>=query2(i,mid)) r=mid,L=mid;
else l=mid+1;
}
if(L==-1) continue;
l=i,r=n;
register int R=i;
while(l<r){
mid=(l+r)>>1;
if(query1(i,mid)>query2(i,mid)) r=mid,R=mid;
else l=mid+1;
}
if(query1(i,l)>query2(i,l)) R=l;
ans+=(R-L);
}
printf("%lld\n",ans);
return 0;
}