题目大意
有一个数字串长度为n,输入顺序为非递减,给出一个区间[L,R],要求算出区间中某个出现次数最多的数,答案为它出现的次数。
1. N<105 , 区间数cas<105
2. 多组测试,以0结尾
解题思路
因为数组是非递减序列,所以可以将数组分段。(也叫游程编码,Run Length Encoding RLE)
1.扫描一遍数组,求如下:
- count[ i ] : 表示第 i 段中数字出现的次数总和
- num[ p ] : 表示位置P所在段的编号
- Left [ p ] , Right[ p ] : 分别表示P所在段的左右端点的位置(这里的位置都是指的在原数组中的位置)
2. 根据count 数组我们可以建立RMQ,每一个数据段都有一个相应出现的次数。
3. 对于给出的区间 [ L, R],按照如下进行求解:
-num[L] = num[R] : 表示给出的区间在一个段落,那么ans = R - L +1;
- 否则 结果至少是L到L所在段的最右端距离 和 R所在段的最左端到R的距离 的最大值,ans = max(Right[L]-L+1, R-Left[R]+1);
- 如果 num[R] > num[L]+1 : 那么说明这个区间中至少有3个段,所以ans = max(ans, RMQ(num[L]+1, num[R]-1)), 在RMQ中找出中间段落的最大值区间为:num[L]+1 到 num[R]-1.
代码献上
#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
using namespace std;
const int MAX_N = 100000;
int a[MAX_N+1],coun[MAX_N+1],num[MAX_N+1];
int Left[MAX_N+1],Right[MAX_N+1],d[MAX_N][20];
//这里采用Sparse-Table 基于稀疏表的RMQ
void RMQ_init(int n)
{
for(int i = 0; i<n; i++) d[i][0] = coun[i];
for(int j = 1; (1<<j)<=n; j++)
for(int i = 0; i+(1<<j) -1<n; i++)
d[i][j] = max(d[i][j-1], d[i+(1<<j-1)][j-1]);
}
int RMQ(int L, int R)
{
int k = 0;
while((1<<(k+1)) <= R-L+1) k++;
return max(d[L][k],d[R-(1<<k)+1][k]);
}
int main()
{
int n,cas;
while(1)
{
scanf("%d",&n);
if(n == 0) break;
scanf("%d",&cas);
for(int i = 0; i<n; i++)
scanf("%d",&a[i]);
int total = 0;
//一遍扫描
for(int i = 0; i<n; )
{
int s = upper_bound(a,a+n,a[i]) - lower_bound(a,a+n,a[i]);
coun[total] = s;
for(int j = i ; j<i+s; j++){
num[j] = total;
Left[j] = i;
Right[j] = i+s-1;
}
i = i+s;
total++;
}
//建立RMQ
RMQ_init(total);
//求解
for(int i = 0; i<cas; i++){
int l,r;
scanf("%d%d",&l,&r);
l = l-1;
r = r-1;
int ans;
if(num[l] == num[r]) ans = r-l+1;
else ans = max(Right[l]-l+1, r-Left[r]+1);
if(num[r]>num[l]+1) ans = max(ans, RMQ(num[l]+1, num[r]-1));
printf("%d\n",ans);
}
}
return 0;
}
时间: 2024-11-08 21:41:56