题目类型 RMQ
题目意思
给出最多包含100000个数的非递减数列 有最多100000次询问 每次询问 第 L 个数到第 R 个数之间最长的每个数都相同的连续子序列是多长
解题方法
可以使用ST算法
首先预处理 把原数列连续相同部分压缩成一个整数 例如 1 1 1 2 2 3 3 3 3 压缩成 3 2 4 并记录压缩后每个数对应的原子序列的开头位置
然后预处理一个数组 dp[i][j] 表示从第 i 个位置开始 (1<<j) 个数这个范围内的最大值
那么对于一个询问 L->R 首先求出 L这个位置往后还有多少是相同的 再求出从R这个位置往前有多少个相同的 记录它们的最大值与中间那些连续相同段对应的数进行比较 具体看代码
参考代码 - 有疑问的地方在下方留言 看到会尽快回复的
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
const int maxn = 1e5 + 10;
const int INF = 1<<29;
const double e = 2.71828;
int a[maxn], b[maxn], num[maxn], sta[maxn];
int dp[maxn][20];
int main() {
//freopen("in", "r", stdin);
int n, q;
while(scanf("%d%d", &n, &q) == 2) {
memset(dp, 0, sizeof(dp));
for( int i=0; i<n; i++ ) {
scanf("%d", &a[i]);
}
int pre = a[0], cut = 1;
int tn = 0; //tn表示有多少不同的相同连续子段
sta[0] = 0; //这个数组保存每个相同连续子段的开始位置
for( int i=1; i<n; i++ ) {
if(a[i] == pre) cut++;
else {
b[tn] = a[i-1]; //b数组保存每个相同连续子段包含的数字个数
num[tn++] = cut;
sta[tn] = i;
cut = 1;
pre = a[i];
}
}
b[tn] = a[n-1]; num[tn++] = cut;
for( int i=0; i<tn; i++ ) dp[i][0] = num[i]; //预处理dp数组
for( int j=1; (1<<j)<tn; j++ ) {
for( int i=0; i<tn && i+(1<<j)-1<tn; i++ ) {
dp[i][j] = max(dp[i][j-1], dp[i+(1<<j)-1-(1<<(j-1))+1][j-1]);
}
}
for( int i=0; i<q; i++ ) {
int l, r;
scanf("%d%d", &l, &r);
l--, r--;
int L = lower_bound(b, b + tn, a[l]) - b;
int R = lower_bound(b, b + tn, a[r]) - b;
int ans = 0;
if(L == R) ans = max(ans, r-l+1);
else {
ans = max(ans, sta[L+1] - l); // 与L相同的在L到R之间的数的个数
ans = max(ans, r - sta[R] + 1); //与R相同的在L到R之间的数的个数
if(L+1 != R) {
double tk = log(1.0*R-1-(L+1)+1)/log(2.0);
int tmp = tk;
ans = max(ans, max(dp[L+1][tmp], dp[R-1-(1<<tmp)+1][tmp]));
}
}
printf("%d\n", ans);
}
}
return 0;
}