题意
有序列{ Sn },并且 S 单调不降
有Q 组询问,每一组询问为 【l,r】 内出现次数最高的数的出现次数
1≤n,Q≤106 ,多组数据
解法
线段树:
又是这种区间合并,要求分类讨论的题目……
有一个假想的暴力做法:
因为 S 单调不降,所以可以记录每一个数的左右范围,然后二分一下l 在哪个数的范围内,然后往后递推取 max ,直到当前数的右边界大于等于 r ,这样做的极限复杂度是O(logn∗∑Qi=1ri−li+1 )
如果要改成线段树来做,那么记录的东西也要改变:对于当前线段树上的区间 【l,r】 ,记录 len 表示区间内的答案, lz 表示左端点表示的数在区间内的长度, rz 同理
显然当 l=r 时,三者皆为1,。然后我们就要考虑网上合并:
初始情况很容易得知: lenk=max(lenls,lenrs),lzk=lzls,rzk=rzrs
接下来就要考虑怎么合并左右区间:
①. Sl=Smid+1 ,那么说明整个左儿子区间都是同一个数,并且和右儿子区间的左半部分相连,所以有: lzk=lzk+lzrs
②. Smid=Sr ,那么说明整个右儿子区间都是同一个数,并且和左儿子区间的右半部分相连,所以有: rzk=rzk+rzls
③. Smid=Smid+1 ,那么说明区间中间部分是相连的,所以有: lenk=max(lenk,rzls+lzrs)
至于询问操作也是差不多的搞法,弄一个结构体,同时返回三个值,然后合并一下再往上回溯,最后的答案就是三者的 max
PS:
按理说,代码中的 clear 函数加了之后应该变慢的,但是我发现如果加上这个函数只要 170ms ,如果不加就要 260ms ,有大神能解释的就发个评论告诉我呗……
复杂度
O( T∗q∗logn )
代码
#include<iostream>
#include<cstdlib>
#include<cstdio>
#define ls 2*k
#define rs 2*k+1
#define Rint register int
#define Lint long long int
using namespace std;
const int N=100010;
int w[N];
int n,q;
struct Tree
{
struct node
{
int l,r;
int lz,rz,len;
}t[N*4];
struct task
{
int len,lz,rz;
}A;
void build(Rint k,Rint l,Rint r)
{
t[k].l=l,t[k].r=r;
if( l==r )
{
t[k].lz=t[k].rz=t[k].len=1;
return ;
}
int mid=(l+r)/2;
build( ls,l,mid ),build( rs,mid+1,r );
t[k].len=max( t[ls].len,t[rs].len );
t[k].lz=t[ls].lz;
t[k].rz=t[rs].rz;
if( w[mid]==w[mid+1] ) t[k].len=max( t[k].len,t[ls].rz+t[rs].lz );
if( w[l]==w[mid+1] ) t[k].lz+=t[rs].lz;
if( w[mid]==w[r] ) t[k].rz+=t[ls].rz;
}
task query(Rint k,Rint l,Rint r)
{
if( t[k].l==l && t[k].r==r ) return (task){ t[k].len,t[k].lz,t[k].rz };
if( r<=t[ls].r ) return query( ls,l,r );
else
if( l>=t[rs].l ) return query( rs,l,r );
else
{
task L=query( ls,l,t[ls].r ),R=query( rs,t[rs].l,r );
task ret=(task){ max( L.len,R.len ),L.lz,R.rz };
if( w[t[ls].r]==w[t[rs].l] ) ret.len=max( ret.len,L.rz+R.lz );
if( w[l]==w[t[rs].l] ) ret.lz+=R.lz;
if( w[t[ls].r]==w[r] ) ret.rz+=L.rz;
return ret;
}
}
void clear(Rint k,Rint l,Rint r)
{
t[k].l=0,t[k].r=0;
if( l==r )
{
t[k].lz=t[k].rz=t[k].len=0;
return ;
}
int mid=(l+r)/2;
clear( ls,l,mid ),clear( rs,mid+1,r );
}
}T;
int main()
{
int l,r;
while( scanf("%d",&n)!=EOF )
{
if( !n ) break ;
scanf("%d",&q);
for(int i=1;i<=n;i++) scanf("%d",&w[i]);
T.build( 1,1,n );
while( q-- )
{
scanf("%d%d",&l,&r);
T.A=T.query( 1,l,r );
printf("%d\n",max( T.A.len,max( T.A.lz,T.A.rz ) ));
}
T.clear( 1,1,n );
}
return 0;
}