题目大意
一个长度为 n 的序列,对于每个位置 i 的数 ai都有一个优美值,其定义是:找到序列中最长的一段 [l, r],满足 l ≤ i ≤ r,且 [l, r] 中位数为 ai (我们比较序列中两个位置的数的大小时,以数值为第一关键字,下标为第二关键字比较。这样的话 [l, r] 的长度只有可能是奇数),r - l+ 1 就是 i 的优美值。
接下来有 Q 个询问,每个询问 [l, r] 表示查询区间 [l, r] 内优美值的最大值。
输入格式
- 第一行输入 n 接下来 n 个整数,代表 ai。
- 接下来一行为整数 Q,代表有 Q个区间需要查询。
- 接下来 Q行,每行两个整数 l,r(l≤r),表示区间的左右端点。
输出格式
- 对于每个区间的询问,输出答案
样例
样例输入
8
16 19 7 8 9 11 20 16
8
3 8
1 4
2 3
1 1
5 5
1 2
2 8
7 8
样例输出
7
3
1
3
5
3
7
3
算法分析
- 先来分析样例 发现3-8区间内居然出现了 7 的优美值 然后稍微一think就知道每个数优美值是确定的不会根据询问的区间改变而改变
- 之前曾经做过一道类似的题 刷子
- 虽然两个题题面毫无共通之处 但是我们只要仔细分析样例就会发现这个的思路似乎好像跟刷子有点类似啊??
- 首先肯定是要找中位数 因为每个a[i]的优美值显然是可以预处理的 , 而预处理的根本就是找以a[i]为中位数的最长序列
- 我们一样用两个数组维护 一个表示向左有j个比a[i]小的数的区间长度 一个表示向右有j个比a[i]小的数的区间长度(不算a[i])
- 如果要保证a[i]为中位数的话,就让左面有j个比它小的,右面有-j个比它小的不就好了?
- 所以我们枚举j(从1-i开始枚举 因为左边可以有-j个比它小的右边有j个比它大的)
代码展示:
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5+10;
int a[maxn];
int cnt;
int L[maxn],R[maxn];
int w[maxn];
int main(){
int n;scanf("%d",&n);
for(int i = 1;i <= n;++i)scanf("%d",&a[i]);
for(int i = 1;i <= n;++i){
memset(L,-1,sizeof(L));L[n] = 0;
memset(R,-1,sizeof(R));R[n] = 0;
cnt = 0;
for(int j = i-1;j >= 1;--j){
if(a[j] <= a[i])cnt--;
if(a[j] > a[i])cnt++;
L[cnt+n] = i-j;//因为可能出现负值所以整体平移n
}
cnt = 0;
for(int j = i+1;j <= n;++j){
if(a[i] <= a[j])cnt++;
if(a[i] > a[j])cnt--;
R[cnt+n] = j-i;
}
for(int j = 1-i;j <= i-1;++j){
if(L[n+j] >= 0 && R[n-j] >= 0)
w[i] = max(w[i],L[n+j] + R[n-j] + 1);//左边的区间值加上右边的区间值 加上a[i]自己
}
}
int Q;scanf("%d",&Q);
while(Q--){
int l,r;scanf("%d%d",&l,&r);
int ans = 0;
for(int i = l;i <= r;++i){
ans = max(ans,w[i]);//求最值
}
printf("%d
",ans);
}
return 0;
}