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; }