https://www.luogu.com.cn/problem/UVA11235
没看到多测调了半天
每组数据给定\(n,q\)。接下来给出一个长度为\(n\)的不降序列\(A\)。接下来\(q\)次询问,每次询问给定\(l,r\),求\(A_{l\sim r}\)中出现最多的那个数出现了多少次。
\(1\le n,q \le 10^5\)。
序列不降,意味着一个数在序列中一定是连续存在的。
那么我们可以把连续相同的元素分成一段(游程编码)。
设一共分了\(len\)段,第\(i\)段的左边界是\(l\),右边界是\(r\)。\(A_i\)在第\(num[i]\)段。
查询时,对于输入的\(x,y\)。我们的答案就是下面三种情况的最大值:
- \(x\sim x\)所在组的右边界,即\(r[num[x]]-x+1\)。
- \(y\)所在组的左边界\(\sim y\),即\(y-l[num[y]]+1\)。
- \(x\)所在组的下一组\(\sim y\)所在组的上一组中,最大组的大小,即\(\max\limits_{num[x]<i<num[y]}(r[i]-l[i]+1)\)。这一操作可以用ST表来维护。
实现细节:
- 如果\(num[x]=num[y]\)(\(x,y\)在同一组),则直接输出\(y-x+1\)。
- 否则,答案就是上面\(3\)种情况的最大值,但需要注意调用
query(num[x]+1,num[y]-1)
,可能会出现\(num[x]+1>num[y]-1\)的情况,此时\(query\)需要返回\(0\)。
每个测试点时间复杂度\(O(n\log n+q)\)。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
int n,q,a[100010];
int siz,cur=INT_MIN;
int l[100010],r[100010],lg[100010];
int len,num[100010];
int maxx[100010][30];
void init(){
lg[0]=-1;
for(int i=1;i<=len;i++)
lg[i]=lg[i/2]+1,maxx[i][0]=r[i]-l[i]+1;
for(int i=1;i<=lg[len];i++){
for(int j=1;j+(1<<i)-1<=len;j++){
maxx[j][i]=max(maxx[j][i-1],maxx[j+(1<<(i-1))][i-1]);
}
}
}
int query(int l,int r){
if(l>r) return 0;
int t=lg[r-l+1];
return max(maxx[l][t],maxx[r-(1<<t)+1][t]);
}
int main(){
while(cin>>n>>q){
len=0;
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=1;i<=n;i++){
if(a[i]!=cur){
r[len]=i-1;
l[++len]=i;
cur=a[i];
}
num[i]=len;
}
r[len]=n;
init();
while(q--){
int x,y;
cin>>x>>y;
if(num[x]==num[y]) cout<<y-x+1<<endl;
else cout<<max(max(r[num[x]]-x+1,y-l[num[y]]+1),
query(num[x]+1,num[y]-1))<<endl;
}
}
return 0;
}