https://ac.nowcoder.com/acm/problem/19913

题意:给你一个排列和一个数字b,问数组中有多少连续奇数子序列的中位数为b。

分析:我们首先想到的方法是找到数字b的位置,然后从该位置向两边左右枚举取数判断是否合法,但1e5的数据显然这么做会超时,所以我们适着转换一下思路,对于该题而言,我们只在乎数据相对于数字的大小,而不在乎其具体值多少,那么我们可以把大于b的数字都看成1,而小于b的数字都看成-1,等于b的数字取中看成0,因为b为中位数,所以它左右两边数个数相等,因此该合法区间和为0.

那么我们可以找出b的位置,向左计算数据和,开个桶存放一下数据和的个数(数组预处理加上1e5防止负数),然后向右遍历看是否存放过了,存过了就直接加上前面数据和的个数即可。

比如题目数据:5 7 2 4 3 1 6
可以转化为:1 1 -1 0 -1 -1 1
扫描左边:f[0]=2 f[-1]=1 f[1]=1
扫描右边:f[0]+f[1]+f[2]+f[1]=4
ans: 4

代码:

#include<stdio.h>
#include<algorithm>
#include<string.h>
#include<math.h>
using namespace std;
typedef long long ll;
int i,j,cnt,n,k,t,b,x,tmp,ans,sum;
int a[100005];
int f[200005];
int main(){
    scanf("%d%d",&n,&b);
    for(int i=1;i<=n;i++){
        scanf("%d",&x);
        if(x<b) a[i]=1;
        else if(x==b) tmp=i;
        else a[i]=-1;
    }
    for(int i=tmp;i<=n;i++){//枚举右端点
        sum+=a[i];f[sum+100001]++;//防止负数干扰
    }
    sum=0;
    for(int i=tmp;i>0;i--){//枚举左端点
        sum+=a[i];ans+=f[-sum+100001];
    }
    printf("%lld",ans);
    return 0;
}