#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;
}