优美值

优美值(思维题 \(\star\star \))

  • 一个长度为 \(n\) 的序列,对于每个位置 \(i\) 的数 \(a_i\) 都有一个优美值,其定义是:找到序列中最 长的一段 \([l, r]\),满足 \(l ≤ i ≤ r\),且 \([l, r]\) 中位数为 \(a_i\)(我们比较序列中两个位置的数的大小时, 以数值为第一关键字,下标为第二关键字比较。这样的话 \([l, r]\) 的长度只有可能是奇数),\(r - l + 1\) 就是 \(i\) 的优美值。
  • 接下来有 $Q $个询问,每个询问 \([l, r]\) 表示查询区间 \([l, r]\) 内优美值的最大值。

Input

  • 第一行输入 \(n\) 接下来 \(n\) 个整数,代表 \(a_i\)
  • 接下来一行为整数 \(Q\),代表有 \(Q\) 个区间需要查询。
  • 接下来 \(Q\) 行,每行 两个整数 \(l, r(l ≤ r)\),表示区间的左右端点。

Output

  • 对于每个区间的询问,输出答案。

Sample Input

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

Sample Output

7
3
1
3
5
3
7
3

Hint

  • 对于 \(30\%\) 的数据,满足 \(n,Q ≤ 50\)
  • 对于 \(70\%\) 的数据,满足 \(n,Q≤2000\)
  • 对于所有数据,满足 \(n≤2000,Q≤100000,a_i≤200\)
  • 来源:\(20180715\) ,推荐:洛谷P1114 “非常男女”计划

分析

  • \(a[i]\) 要想成为中位数,则区间内比它小的数和比它大的数要一样多,我们从 \(i\) 开始向左扫描,如果遇到比 \(a[i]\) 大的数 \(cnt++\) ,遇到小于等于 \(a[i]\) 的数就 \(cnt--\) ,这样我们就很容易维护左边相互抵消后比 \(a[i]\) 大的区间长度,具体细节见代码,同样我们也能处理处右边的情况。
  • 处理完两个数组后,要想 \(a[i]\) 成为中位数,则,如果左边大小抵消后比 \(a[i]\) 大的数还有 \(cnt\) 个,则,右边需要大小抵消后比 \(a[i]\) 大与等于的数应该有 \(-cnt\) 个,即右边比 \(a[i]\) 小的数有 \(cnt\) 个。

Code

#include <bits/stdc++.h>
const int maxn=2e3+5;
int n, a[maxn], w[maxn];
int L[maxn<<1],R[maxn<<1];//L[cnt]:当前处理数左边比它大的数-比它小的数个数为cnt的最大区间长度
void Init(){
    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;
        int cnt = 0;
        for(int j=i-1;j>=1;--j){
            if(a[j] > a[i]) cnt++;//左边比a[i]大的+1
            if(a[j] <= a[i]) cnt--;//左边比a[i]小的-1,相当于一大一小相互抵消
            L[n + cnt] = i - j;//cnt有可能为负,所以整体+n
        }
        cnt = 0;
        for(int j=i+1;j<=n;++j){
            if(a[j] >= a[i]) cnt++;//右边比a[i]大的+1
            if(a[j] < a[i]) cnt--;//右边比a[i]小的-1,相当于一大一小相互抵消
            R[n + cnt] = j - i; //cnt有可能为负,所以整体+n
        }
        for(int j=1-i;j<=i-1;++j)//大小抵消后,a[i]为中位数,左端比a[i]大的为j,右边比它大的为-j
            if(L[n + j] >= 0 && R[n - j] >= 0)//j表示比a[i]大,-j表示比a[j]小
                w[i] = std::max(w[i], L[n + j] + 1 + R[n - j]);
    }
}
void Solve(){    
    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 = std::max(ans, w[i]);
        printf("%d\n", ans);    
    }
}
int main(){
    Init();    
    Solve();
    return 0;
}
posted @ 2020-07-08 11:19  ♞老姚♘  阅读(233)  评论(0编辑  收藏  举报