题目描述

给出1~n的一个排列,统计该排列有多少个长度为奇数的连续子序列的中位数是b。中位数是指把所有元素从小到大排列后,位于中间的数。

题解:

  • 题目没有给数据范围。。。实测1e5+10能过。
  • 由于是 1~n 的一个排列,所以不用担心数字重复的问题。而且,由于是连续子序列,所以满足答案的序列必须是包含数字 b 的左右区间。
  • 由于是求中位数,所以除了 b 以外的数的具体大小并不重要,重要的是该数比 b 大还是比 b 小。我们用 a[i] = -1 代表该数比 b 小,用 a[i] =1 代表该数比 b 大。
  • 连续区间,很容易让人联想到前缀和。在这题中,我们使用前缀和来表示从当前数到 b 的区间中,比 b 大的数和比 b 小的数的状态(数量)。只需要对 a 数组求前缀和,就可以求出以上数量。
  • 如 s[i] = 2, 就代表区间 [i,b的位置-1] (i<b)中比 b 大的数相较于比 b 小的数多两个。
  • 那我们怎么计算结果呢?如果光看左半区间和右半区间,只有当 s[i] == 0 的时候,才能成立,如果左右两个区间合起来看的话,只需要左右两个区间互为相反,即比 b 大和小的数量加起来相等,就成立。

代码:

#include<iostream>

using namespace std;
const int N = 100010;
const int add = 50005;//因为有负数,为了避免负数访问数组,我们加上一个大数!
long long a[N],s[N];
long long cnt[N];
int main(){
    int n,b;
    cin>>n>>b;
    int pos;
    cnt[add]=1;
    for(int i=0;i<n;i++) {
        cin>>a[i];
        if(a[i]>b) a[i]=1;
        else if(a[i]==b) pos=i;
        else a[i]=-1;
    }

    long long res=1;
    for(int i=pos+1;i<n;i++){
        s[i]=a[i]+s[i-1];
        cnt[s[i]+add]++;
        if(s[i]==0) res++;
    }

    for(int i=pos-1;i>=0;i--){
        s[i]=a[i]+s[i+1];
        res+=cnt[-s[i]+add];
        //cout<<res<<endl;
    }
    //for(int i=0;i<n;i++) cout<<s[i]<<endl;
    cout<<res;
    return 0;
}