Codeforces D. Friends and Subsequences [RMQ + erfen]

这道题的题目链接

题目的大意是给出两个数组 ai,bi a i , b i ,判断有多少个区间 [l,r] [ l , r ] ,使得数组a在这个区间中的最大值,等于数组B在区间中的最小值

首先不对数组进行操作我们可以用ST表来实现取最值的操作。其次,我们要知道假设固定区间的左端点,最大值是非降的,最小值是非升的,那么有了线性的性质我们就可以二分了。

我们每次固定一个左端点,找出让 MAXni=1ai=MINni=1bi M A X ∑ i = 1 n a i = M I N ∑ i = 1 n b i 的最大位置和最小位置,答案加上这个区间的长度即可。

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#include <vector>
#include <map>
#include <cmath>
#include <iostream>
#define ll long long
#define x first
#define y second
using namespace std;
const int MAXN = 2e5+10;
int da[MAXN][20];
int db[MAXN][20];
int ma[MAXN];
int mb[MAXN];
int A[MAXN],B[MAXN];
void initRMQMAX(int n,int b[])
{
    ma[0] = -1;
    for(int i = 1; i <= n; i++)
    {
        ma[i] = ((i&(i-1)) == 0)?ma[i-1]+1:ma[i-1];
        da[i][0] = b[i];
    }
    for(int j = 1; j <= ma[n]; j++)
        for(int i = 1; i + (1<<j) -1 <= n; i++)
            da[i][j] = max(da[i][j-1],da[i+(1<<(j-1))][j-1]);
}
void initRMQMIN(int n,int b[])
{
    mb[0] = -1;
    for(int i = 1; i <= n; i++)
    {
        mb[i] = ((i&(i-1)) == 0)?mb[i-1]+1:mb[i-1];
        db[i][0] = b[i];
    }
    for(int j = 1; j <= mb[n]; j++)
        for(int i = 1; i + (1<<j) -1 <= n; i++)
            db[i][j] = min(db[i][j-1],db[i+(1<<(j-1))][j-1]);
}
//查询最大值
int amax(int x,int y)
{
    int k = ma[y-x+1];
   // cout<<"check max on:"<<x<<" "<<y<<"get "<<max(da[x][k],da[y-(1<<k)+1][k])<<endl;
    return max(da[x][k],da[y-(1<<k)+1][k]);
}
int bmin(int x,int y)
{
    int k = mb[y - x+1];
    //cout<<"check min on:"<<x<<" "<<y<<"get "<<min(db[x][k],db[y-(1<<k) +1][k])<<endl;
    return min(db[x][k],db[y-(1<<k) +1][k]);
}
int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    int n;
    cin>>n;
    for(int i = 1;i<=n;i++)
    {
        cin>>A[i];
    }
    initRMQMAX(n,A);
    for(int i = 1;i<=n;i++)
    {
        cin>>B[i];
    }
    initRMQMIN(n,B);
    ll ans = 0;
    for(int i = 1;i<=n;i++)
    {
        int l = i;
        int r = n;
        int st = -1;
        while(l <= r)
        {
            int mid = (l +r)/2;
            int aa = amax(i,mid);
            int bb = bmin(i,mid);
            if(aa >= bb)
            {
                if(aa == bb) st = mid;
                r = mid-1;
            }
            else
            {
                l = mid+1;
            }
        }
        if(st == -1) continue;
        l = i,r = n;
        int en = -1;
        while(l <= r)
        {
            int mid = (l + r) /2;
            int aa = amax(i,mid);
            int bb = bmin(i,mid);
            if(aa > bb) r = mid-1;
            else
            {
                if(aa == bb) en = mid;
                l = mid+1;
            }
        }
        if(en == -1) continue;
        ans += (en - st +1);
    }
    cout<<ans<<endl;
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值