hihocoder #1710 : 等差子数列(二分+RMQ)

#1710 : 等差子数列

时间限制: 10000ms
单点时限: 1000ms
内存限制: 256MB

描述

给定N个整数A1, A2, ... AN,小Hi会询问你M个问题。

对于每个问题小Hi给出两个整数L和R(L ≤ R),请你找出[AL, AL+1, AL+2, ... AR]中最长的等差连续子数列,并输出其长度。  

例如[2, 3, 5, 7, 9]中最长的等差连续子数列是[3, 5, 7, 9]长度为4。

输入

第一行包含两个整数N和M。  

第二行包含N个整数A1, A2, ... AN。  

以下M行每行包含两个整数L和R,代表一次询问。

对于30%的数据,1 ≤ N, M ≤ 1000  

对于100%的数据,1 ≤ N, M ≤ 100000 0 ≤ Ai ≤ 10000000

输出

依次对于每个询问输出一个整数,代表答案。

样例输入
6 2  
1 2 3 5 7 9  
2 6  
1 4
样例输出
4  
3

 首先离散化,把所有的满足等差数列的区间都分离出来记为L[i],R[i]

用RMQ存储离散化后的满足等差数列的区间最大值

对于每个查询,二分查找在离散化区间中的标号,对于中间的一大段RMQ查询最大值,再和两边剩余的求个max即可

比如下面这张图,设每次的查询为l,r,离散化后的找出的左右端点坐标分别是x和y,对于x和y中间的这一段RMQ查询,然后和两边剩下的取个最大值即可


#include<bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
const int N=1e5+50;
typedef long long ll;
int n,m,a[N],tot=1,L[N],R[N],st[N][20];
ll sum[N],sum2[N];
int query(int x,int y)
{
    if(y<x)return 0;
    int len=log2(y-x+1.0);
    return max(st[x][len],st[y-(1<<len)+1][len]);
}
int solve(int l,int r)
{
    //二分找出左右端点
    int x=upper_bound(R+1,R+tot+1,l)-R;
    int y=upper_bound(L+1,L+tot+1,r)-L-1;
    //左右端点被一个大区间包含
    if(L[y]<l||R[x]>r)return r-l+1;
    return max(max(R[x]-l+1,r-L[y]+1),query(x+1,y-1));
}
void RMQ()
{
    for(int i=1;i<=tot;i++)st[i][0]=R[i]-L[i]+1;
    for(int j=1;j<20;j++)
        for(int i=1;i+(1<<j)-1<=n&&i<=n;i++)
            st[i][j]=max(st[i][j],max(st[i][j-1],st[i+(1<<j-1)][j-1]));
}

int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1; i<=n; i++)
    {
        scanf("%d",&a[i]);
        sum[i]=sum[i-1]+a[i];
    }
    int l=1,r;
    for(int i=1;i<=n;i++)
    {
        if(2*(sum[i]-sum[l-1])!=(i-l+1)*(a[l]+a[i]))// 等差数列公式 n*(a1+an)/2
        {
           L[tot]=l;
           R[tot++]=i-1;
           l=i-1;
        }
    }
    if(l<n)
    {
        L[tot]=l;
        R[tot++]=n;
    }
    L[tot]=n+1;
    R[tot]=n+1;
    RMQ();
    while(m--)
    {
        scanf("%d%d",&l,&r);
        if(r-l+1==1)puts("1");
        else
            //长度>=2的区间最小是2
        cout<<max(solve(l,r),2)<<endl;
    }
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值