数列问题(二分+思维)好题

二分+思维好题


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;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值