题目链接:点击打开链接
题目大意:
给你一段连续的递增序列,然后多次查询,每次查询输出查询区间内相同的数出现的最多的次数。
解题思路:
学校内部的比赛碰到的题目,题目看起来异常的简单,基本确定是线段树。刚开始就是想不出来维护的办法,甚至想到用map储存每个点出现次数吧。后来突然想到了什么左连续 最长右连续最长和区间最长连续。思路顿开,遂A。
线段树每个节点维护左连续最长,右连续最长,和区间连续最长,从下往上推。pushup过程不算太难,就是注意左区间右连续和右区间左连续合并要注意值相等才行。剩下可能就查询麻烦一些了,查询的话如果左区间右端点和右区间左端点相同是要加上一种查询情况的,最后取查询出来的最大值就好(听大佬说还可以用什么RMQ算法,还不会, 就先这样啦)。细节看代码,
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <cstdlib>
#include <cmath>
#include <vector>
#include <queue>
#include <map>
#include <algorithm>
#include <set>
#include <functional>
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
int n,m;
struct tree
{
int l,r,mid;
int ls,rs,ms;
}t[500000];
int a[200000];
void pushup(int rt) //从下往上更新
{
if((t[rt<<1].ls==t[rt<<1].r-t[rt<<1].l+1)&&a[t[rt<<1].r]==a[t[rt<<1|1].l])
t[rt].ls=t[rt<<1].ls+t[rt<<1|1].ls;
else
t[rt].ls=t[rt<<1].ls;
if((t[rt<<1|1].rs==t[rt<<1|1].r-t[rt<<1|1].l+1)&&(a[t[rt<<1|1].l]==a[t[rt<<1].r]))
t[rt].rs=t[rt<<1|1].rs+t[rt<<1].rs;
else
t[rt].rs=t[rt<<1|1].rs;
t[rt].ms=max(t[rt<<1].ms,t[rt<<1|1].ms);
if(a[t[rt<<1].r]==a[t[rt<<1|1].l]) //只有相等才能合并
t[rt].ms=max(t[rt].ms,t[rt<<1].rs+t[rt<<1|1].ls);
}
void build(int l,int r,int rt) //建立线段树
{
int mid=(l+r)>>1;
t[rt].l=l;t[rt].r=r;
t[rt].mid=mid;
if(l==r)
{
t[rt].ls=1;
t[rt].rs=1;
t[rt].ms=1;
return ;
}
build(l,mid,rt<<1);
build(mid+1,r,rt<<1|1);
pushup(rt);
}
int query(int p,int q,int rt) //查询稍微麻烦一些
{
if(t[rt].l==p&&t[rt].r==q)
return t[rt].ms;
if(q<=t[rt].mid)
return query(p,q,rt<<1);
if(p>t[rt].mid)
return query(p,q,rt<<1|1);
else
{
int aa=query(p,t[rt].mid,rt<<1);
int bb=query(t[rt].mid+1,q,rt<<1|1);
int cc=-1;
if(a[t[rt<<1].r]==a[t[rt<<1|1].l]) //特判情况
cc=min(t[rt<<1].rs,t[rt].mid-p+1)+min(t[rt<<1|1].ls,q-t[rt].mid);
return max(max(aa,bb),cc);
}
}
int main()
{
while(scanf("%d",&n)!=EOF)
{
if(n==0)
break;
scanf("%d",&m);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
build(1,n,1);
int p,q;
for(int i=0;i<m;i++)
{
scanf("%d%d",&p,&q);
int g=query(p,q,1);
printf("%d\n",g);
}
}
}