Codefores 689D. Friends and Subsequences(二分+st)

题意:求max< a>(l,r)与min< b>(l,r)相等的对数。
用st表保存max< a>和min< b>的信息。
但是这样子如果枚举左端点再枚举右端点,n2绝对t了。
我刚开始写的是二分区间(递归写的),wa了,找不出哪里错了,不过比起下面这种又不好写又不好查代码。

所以要利用区间性质进行二分,
利用序列a的最大值单调非递减,序列b的最小值单调非递增,二分两次。
第一次求出最大值与最小值相等时j的最小值p,第二次求出最大值与最小值相等时j的最大值q,区间[p,q]里所有的点都是满足答案的。
只简单说明一下第一次二分
此时左起点为i,二分右端点,如果区间[i,mid]的最小值大于最大值,说明有希望,令l=mid+1;
最小值等于最大值,说明所求的mid要么是所求p,要么比所求p大,
所以r=p=mid;
如果最小值小于最大值,没救了,赶紧r=mid;
(这里的r是不可取的端点值)

#include <iostream>
#include <stdio.h>
#include <algorithm>
#include <stdlib.h>
#include <stack>
#include <vector>
#include <string.h>
#include <queue>
#define msc(X) memset(X,-1,sizeof(X))
#define ms(X) memset(X,0,sizeof(X))
typedef long long LL;
using namespace std;
int fx[200003][20],fn[200003][20],a[200003],b[200003],n;
int judge(int st,int ed)
{
    if(ed>n) return -1;
    int k=0;
    while((1<<(k+1))<=ed-st+1) k++;
    return min(fn[st][k],fn[ed-(1<<k)+1][k])-max(fx[st][k],fx[ed-(1<<k)+1][k]);
}
int main(int argc, char const *argv[])
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++) {scanf("%d",a+i);fx[i][0]=a[i];}
    for(int i=1;i<=n;i++) {scanf("%d",b+i);fn[i][0]=b[i];}
    for(int j=1;(1<<j)<=n;j++)
        for(int i=1;i<=n;i++)
            if(i+(1<<(j-1))<=n)
    fx[i][j]=max(fx[i][j-1],fx[i+(1<<(j-1))][j-1]),
    fn[i][j]=min(fn[i][j-1],fn[i+(1<<(j-1))][j-1]);
    else fx[i][j]=fx[i][j-1],fn[i][j]=fn[i][j-1];
    LL cnt=0;
    for(int i=1;i<=n;i++)
    {
        int st=-1,l=i,r=n+1,ed;
        while(l<r)
        {
            int mid=(l+r)>>1;
            if(judge(i,mid)<0) r=mid;
            else if(judge(i,mid)>0) l=mid+1;
            else r=st=mid;
        }
        if(st==-1) continue;
        l=i,r=n+1;
        while(l<r)
        {
            int mid=(l+r)>>1;
            if(judge(i,mid)<0) r=mid;
            else if(judge(i,mid)>0) l=mid+1;
            else ed=mid,l=mid+1;
        }
        cnt+=ed-st+1;
    }
    printf("%I64d\n",cnt );
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值