↑这是题面,赵老师%%%
我们很容易就能想到缩点。将连续的一段数字缩为一个点。然后在点上跑st表。
类似于分块的想法
如果有零碎的块,就暴力计算。中间的块使用st表O(1)计算
总时间复杂度(nlogn)
O(n)分块。
O(nlogn)预处理。
O(1)查询。
#include<iostream>
#include<cstdio>
using namespace std;
int in[100000],n;
struct node
{
int x;
int y;
int len;
};//块的结构体
node k[1000000];//块
int ll;//块的个数
int st[1000010][20];//在块上的st表
int log[1000010];//以2为底的log
int pow[20];//二的n次方
int flag[1000010];//原数组中第i项属于第几个块
void st_()//st表初始化
{
log[0]=0;
log[1]=0;
for(int i=2;i<=ll;i++)
log[i]=log[i>>1]+1;//递推log
pow[0]=1;
for(int i=1;i<=log[ll];i++)
pow[i]=pow[i-1]<<1;//处理2^N次方
for(int i=1;i<=ll;i++)
st[i][0]=k[i].len;
for(int i=1;i<=log[ll];i++)//正常的st表
for(int j=1;j+pow[i]-1<=ll;j++)
st[j][i]=max(st[j][i-1],st[j+pow[i-1]][i-1]);
}
void first_work()//分块
{
int now=-0x7fffffff;
for(int i=1;i<=n;i++)
{
if(now!=in[i])//分块。遇到一个不同于当前的数据。则表示要进行下一步的分块
{
k[ll].y=i-1;//前一个块的右节点
k[ll].len=k[ll].y-k[ll].x+1;//前一个块的长度
k[++ll].x=i;//现在块的左节点
now=in[i];//更新
}
flag[i]=ll;//处理第i项在那一块里
}
k[ll].y=n;
k[ll].len=k[ll].y-k[ll].x+1;
}
int st_check(int begin,int end)//连续的块查询。
{
if(begin>end)//这种情况发生在两个零块相邻。所以中间没有整块
return -0x7fffffff;//返回无穷小
int l=end-begin+1;
return max(st[begin][log[l]],st[end-pow[log[l]]+1][log[l]]);//O(1)查询
}
int bl_check(int begin,int end)//零块查询
{
return end-begin+1;//直接计算,因为一个块中没有不同的数字
}
int main()
{
scanf("%d",&n);
int m;
scanf("%d",&m);
for(int i=1;i<=n;i++)
scanf("%d",&in[i]);
first_work();
st_();
int a,b;
int ans;
for(int i=1;i<=m;i++)
{
ans=-0x7ffffff;
scanf("%d%d",&a,&b);
ans=max(ans,bl_check(a,k[flag[a]].y));//分成两个零块和一堆连续的整块
ans=max(ans,st_check(flag[a]+1,flag[b]-1));
ans=max(ans,bl_check(k[flag[b]].x,b));
printf("%d\n",ans);
}
}