POJ - 3368 Frequent values
UVA - 11235 Frequent values
题目
题目给出一个长度为 n 非递减序列,q 次询问区间最长连续相等序列长度。
(
1
<
=
n
,
q
<
=
1
e
5
1 <= n,q <= 1e5
1<=n,q<=1e5)
分析
区间问题,线段树肯定可以做,不过应该有点麻烦。可以看出题目是离线询问,没有更新部分,只要处理一次序列即可。
因为给出序列为非递减序列,也就是可以分成一段一段的,即游程编码,例如:
-1,1,1,2,2,2,4,
编码为:
(-1, 1), (1, 2), (2, 3), (4, 1)
这样问题就变为在所给区间完整包含的段里面取最大值,两边的段分别考虑。图示:
中间的就是ST表找最值,两边的直接比长度即可。
代码
#include <cstdio>
#include <iostream>
#include <cmath>
#include <algorithm>
#include <cstring>
using namespace std;
#define d(x) cout<<(x)<<endl;
const int N = 1e5 + 10;
int n, q;
int a[N], st[N][20];
int val[N], cnt[N]; // 每段的值,每段的长度
int id[N], l[N], r[N]; // i 所在段的编号,左端点,右端点
void rmq(){
for (int i = 1; i <= n; i++){
st[i][0] = cnt[i];
}
for (int j = 1; (1 << j) <= n; j++){
for (int i = 1; i + (1 << j) - 1 <= n; i++){
st[i][j] = max(st[i][j - 1], st[i + (1 << (j - 1))][j - 1]);
}
}
}
int solve(int x, int y){
if(id[x] == id[y]){
return y - x + 1;
}
int ans = max(r[x] - x + 1, y - l[y] + 1);
int L = id[x] + 1, R = id[y] - 1;
if(L <= R){
int k = log2(R - L + 1);
ans = max(ans, max(st[L][k], st[R - (1 << k) + 1][k]));
}
return ans;
}
int main()
{
while(scanf("%d", &n)){
if(!n)
break;
scanf("%d", &q);
int k = 1, x, y; // 段数
for(int i = 1; i <= n; i++){
scanf("%d", &a[i]);
}
a[n + 1] = a[n] + 1; // 处理最后一段
int start = 0;
for (int i = 1; i <= n + 1; i++){
if(i == 1 || a[i] > a[i-1]){
if(i > 1){
cnt[k++] = i - start;
for (int j = start; j < i; j++){
id[j] = k - 1;
l[j] = start;
r[j] = i - 1;
}
}
start = i;
}
}
rmq();
while(q--){
scanf("%d%d", &x, &y);
printf("%d\n", solve(x, y));
}
}
return 0;
}