题目描述

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

输入描述:

第一行为两个正整数n和b ,第二行为1~n 的排列。

输出描述:

输出一个整数,即中位数为b的连续子序列个数。 

题解

对于这道题,我们想求的是一个大于k和小于k的数字的数量相等且包含k的区间,我们可以把大于k的数字统一看成1,小于k的数字看作-1,k看作0,来求一个前缀和序列。
比如我们用样例来举一个例子:
7 4
原数列:            5 7 2 4 3 1 6
对应的前缀和数列:0 1 2 1 1 0 -1 0
我们要求的序列必定满足一下条件:
1. 若序列为l到r,那么l-1与r的前缀和必定相等。因为他们之间经过了相同数量的大于k和小于k的数字,此段和应为0。
2. 必须保证l-1和r不在k的同一侧。
所以我们就可以愉快的写一个前缀和代码啦~
我把需要注意的地方在代码里写上了注释,大家在写的时候一定要注意细节哟

完整代码

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N=1e5;
int a[100500];
int sum[200500];//用来记录有多少位置的值为m,开两倍是为了防止负数下标,把所有前缀和+N,也可以直接开一个map
int n,k;
int main(){
    scanf("%d%d",&n,&k);
    bool flag=false;//判断是否过了k这个数字
    int ans=0;//答案
    sum[N]=1;//N相当于0+N,及前缀和为0的,最开头的地方即什么数也没有的时候前缀和是0
    for(int i=1;i<=n;++i){
        int num;
        scanf("%d",&num);
        if(num==k){
            flag=true;
            a[i]=a[i-1];
        }
        else a[i]=a[i-1]+(num>k?1:-1);//计算前缀和
        if(flag)ans+=sum[a[i]+N];//如果已经过了k了,那么我们就可以计算了
        if(!flag)sum[a[i]+N]++;//之有没有过k的地方才可以做l,如果没有这句话你就要判断奇偶了
        //对于样例来理解这句代码
        //样例3这个数字的前缀和为0,6也为0,但从3到6并不是合法区间,因为他们并不位于k两侧
    }
    printf("%d",ans);
}
当然,大家也可以按照雨巨讲的那种方法来实现,即从k往前做一个后缀数组,k往后做一个前缀数组